aboutsummaryrefslogtreecommitdiff
path: root/src/blog
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2023-06-16 15:54:30 +0200
committerSebastiano Tronto <sebastiano@tronto.net>2023-06-16 15:54:30 +0200
commit34587f1c10d5fe0fe32b52f5032665ae8ce87d1a (patch)
tree3e2609c09527180caf61541629efb3e74696e4e8 /src/blog
parenta5d66cb6da3f6ab3b79898769b4d118c8b9e498b (diff)
downloadsebastiano.tronto.net-34587f1c10d5fe0fe32b52f5032665ae8ce87d1a.tar.gz
sebastiano.tronto.net-34587f1c10d5fe0fe32b52f5032665ae8ce87d1a.zip
Added blog post
Diffstat (limited to 'src/blog')
-rw-r--r--src/blog/2023-06-16-regex/regex.md201
-rw-r--r--src/blog/blog.md1
-rw-r--r--src/blog/feed.xml7
3 files changed, 209 insertions, 0 deletions
diff --git a/src/blog/2023-06-16-regex/regex.md b/src/blog/2023-06-16-regex/regex.md
new file mode 100644
index 0000000..e6c46f1
--- /dev/null
+++ b/src/blog/2023-06-16-regex/regex.md
@@ -0,0 +1,201 @@
1# UNIX text filters, part 0 of 3: regular expressions
2
3One of the most important features of UNIX and its descendants, if
4not *the* most important feature, is input / output redirection:
5the output of a command can be displayed to the user, written to a
6file or used as the input for another command seamlessly, without
7the the program knowing which of these things is happening. This
8is possible because most UNIX programs use *plain text* as their
9input/output language, which is understood equally well by the three
10types of users - humans, files and other running programs.
11
12Since this is such a fundamental feature of UNIX, I thought it would
13be nice to go through some of the standard tools that help the user
14take advantage of it. At first I thought of doing this as part of
15my *man page reading club* series, but in the end I decided to give
16them their own space. My other series has also been going on for
17more than a year now, so it is a good time to end it and start a
18new one.
19
20Let me then introduce you to: **UNIX text filters**.
21
22## Text filters
23
24For the purpose of this blog series, a *text filter* is a program
25that reads plain text from standard input and writes a modified,
26or *filtered* version of the same text to standard output. According
27to the introductory paragraph, this definition includes most UNIX
28programs; but we are going to focus on the following three, in
29increasing order of complexity:
30
31* grep
32* sed
33* awk
34
35In order to unleash the true power of these tools, we first need
36to grasp the basics of
37[regular expressions](https://en.wikipedia.org/wiki/Regular_expression).
38And what better way to do it than following the dedicated
39[OpenBSD manual page](https://man.openbsd.org/OpenBSD-7.3/re_format)?
40
41## (Extended) regular expressions
42
43Regular expressions, or regexes for short, are a convenient way to
44describe text patterns. They are commonly used to solve genering
45string matching problems, such as determining if a given piece
46of text is a valid URL. Many standard UNIX tools, including the three
47we are going to cover in this series, support regexes.
48
49Let's deal with the nasty part first: even within POSIX, there is
50not one single standard for regular expressions; there are at least
51two of them: Basic Regular Expressions (BREs) and Extended Regular
52Expressions (ERE). As it always happens when there is more than one
53standard for the same thing, other people decided to come up with
54another version to replace all previous "standards", so we have also
55[PCREs](https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions),
56and probably more. [Things got out of hand quickly](https://xkcd.com/927).
57
58In this post I am going to follow the structure of
59[re_format(7)](https://man.openbsd.org/OpenBSD-7.3/re_format) and
60present *extended* regular expresssions first. After that I'll point
61out the differences with *basic* regular expressions.
62
63The goal is not to provide a complete guide to regexes, but rather
64an introduction to the most important features, glossing over the
65nasty edge-cases. Keep also in mind that I am in no way an expert
66on the subject: we are learning together, here!
67
68### The basics
69
70You can think of a regular expression as a *pattern*, or a *rule*,
71that describes which strings are "valid" (they are *matched* by the
72regular expression) and which are not. As a trivial example, the
73regular expression `hello` matches only the string "hello". A less
74trivial example is the regex `.*` that matches *any* string. I'll
75explain why in a second.
76
77Beware not to confuse regular expressions with *shell globs*, i.e.
78the rules for shell command expansion. Although they use similar
79symbols to achieve a similar goal, they are not the same thing. See
80[my post on sh(1)](../2022-09-13-sh-1) or
81[glob(7)](https://man.openbsd.org/OpenBSD-7.3/glob.7) for an
82explanation on shell globs.
83
84### General structure and terminology
85
86A general regex looks something like this:
87
88```
89piece piece piece ... | piece piece piece ... | ...
90```
91
92A sequence of *pieces* is called a *branch*, and a regex is a
93sequence of branches separated by pipes `|`. Pieces are not separated
94by spaces, they are simply concatenated.
95
96The pipes `|` are read "or": a regex matches a given string if any
97of its branches does. A branch matches a given string if the latter
98can be written as a sequence of strings, each matching one of the
99pieces, in the given order.
100
101Before going into what pieces are exactly, consider the following
102example:
103
104```
105hello|world
106```
107
108This regex matches both the string "hello" and the string "world",
109and nothing else. The pieces are the single letters composing the
110two words, and as you can see they are juxtaposed without spaces.
111
112But what else is a valid piece? In general, a piece is made up of
113an *atom*, optionally followed by a *multiplier*.
114
115### Atoms
116
117As we have already seen, the most simple kind of atom is a single
118character. The most *general* kind of atom, on the other hand, is
119a whole regular expression enclosed in parentheses `()`. Yes, regexes
120are recursive.
121
122There are some special characters: for example, a single dot `.`
123matches *any* single character. The characters `^` and `$` match
124an empty string at the beginning and at the end of a line, respectively.
125If you want to match a special character as if it was regular, say
126because you want to match strings that represent values in the
127dollar currency, you can *escape* them with a backslash. For example
128`\$` matches the string "$".
129
130The last kind of atoms are *bracket expressions*, which consist of
131lists of characters enclosed in brackets `[]`. A simple list of
132characters in brackets, like `[xyz]`, matches any character in the
133list, unless the first character is a `^`, in which case it matches
134every character *not* in the list. Two characters separated by a
135dash `-` denote a range: for example `[a-z]` matches every lowercase
136letter and `[1-7]` matches all digits from 1 to 7.
137
138You can also use cetain special sets of characters, like `[:lower:]`
139to match every lowercase letter (same as `[a-z]`), `[:alnum:]` to
140match every alphanumeric character or `[:digit:]` to match every
141decimal digit. Check the
142[man page](https://man.openbsd.org/OpenBSD-7.3/re_format)
143for the full list.
144
145### Multipliers
146
147The term "multiplier" does not appear anywhere in the manual page, I
148made it up. But I think it fits, so I'll keep using it.
149
150Multipliers allow you to match an atom repeating a specified or
151unspecified amount of times. The most general one is the *bound*
152multiplier, which consists of one or two comma-separated numbers
153enclosed in braces `{}`.
154
155In its most simple form, the multiplier `{n}` repeats the multiplied
156atom `n` times. For example, the regex `a{7}` is equivalent to the
157regex `aaaaaaa` (and it matches the string "aaaaaaa").
158
159The form `{n,m}` matches *any number* between `n` and `m` of copies
160of the preceeding atom. For example `a{2,4}` is equivalent to
161`aa|aaa|aaaa`. If the integer `m` is not specified, the multiplied
162atom matches any string that consists of *at least* `n` copies of
163the atom.
164
165Now we can explain very quickly the more common multipliers `+`,
166`*` and `?`: they are equivalent to `{1,}`, `{0,}` and `{0,1}`
167respectively. That is to say, `+` matches at least one copy of the
168atom, `*` matches any number of copies (including none) and `?`
169matches either one copy or none.
170
171## Basic regular expressions
172
173Basic regular expressions are less powerful than their extended
174counterpart (with one exception, see below) and require more
175backslashes, but it is worth knowing them, because they are used
176by default in some programs (for example [ed(1)](../2022-12-24-ed)).
177The main differences between EREs and BREs are:
178
179* BREs consist of one single branch, i.e. there is no `|`.
180* Multipliers `+` and `?` do not exist.
181* You need to escape parentheses `\(\)` and braces `\{\}` to
182 use them with their special meaning.
183
184There is one feature of BREs, called *back-reference*, that is
185absent in EREs. Apparently it makes the implementation much more
186complex, and it makes BREs more powerful. I noticed the author of
187the manual page despises back-references, so I am not going to learn
188them out of respect for them.
189
190## Conclusion
191
192Regexes are a powerful tool, and they are more than worth knowing.
193But, quoting from the manual page:
194
195```
196 Having two kinds of REs is a botch.
197```
198
199I hope you enjoyed this post, despite the lack of practical examples.
200If you want to see more applications of regular expressions, stay
201tuned for the next entries on grep, sed and awk!
diff --git a/src/blog/blog.md b/src/blog/blog.md
index be9015d..6024e0c 100644
--- a/src/blog/blog.md
+++ b/src/blog/blog.md
@@ -5,6 +5,7 @@
5 5
6## 2023 6## 2023
7 7
8* 2023-06-16 [UNIX text filters, part 0 of 3: regular expressions](2023-06-16-regex)
8* 2023-05-05 [I had to debug C code on a smartphone](2023-05-05-debug-smartphone) 9* 2023-05-05 [I had to debug C code on a smartphone](2023-05-05-debug-smartphone)
9* 2023-04-10 [The big rewrite](2023-04-10-the-big-rewrite) 10* 2023-04-10 [The big rewrite](2023-04-10-the-big-rewrite)
10* 2023-03-30 [The man page reading club: dc(1)](2023-03-30-dc) 11* 2023-03-30 [The man page reading club: dc(1)](2023-03-30-dc)
diff --git a/src/blog/feed.xml b/src/blog/feed.xml
index 58df461..f7582dd 100644
--- a/src/blog/feed.xml
+++ b/src/blog/feed.xml
@@ -9,6 +9,13 @@ Thoughts about software, computers and whatever I feel like sharing
9</description> 9</description>
10 10
11<item> 11<item>
12<title>UNIX text filters, part 0 of 3: regular expressions</title>
13<link>https://sebastiano.tronto.net/blog/2023-06-16-regex</link>
14<description>UNIX text filters, part 0 of 3: regular expressions</description>
15<pubDate>2023-06-16</pubDate>
16</item>
17
18<item>
12<title>I had to debug C code on a smartphone</title> 19<title>I had to debug C code on a smartphone</title>
13<link>https://sebastiano.tronto.net/blog/2023-05-05-debug-smartphone</link> 20<link>https://sebastiano.tronto.net/blog/2023-05-05-debug-smartphone</link>
14<description>I had to debug C code on a smartphone</description> 21<description>I had to debug C code on a smartphone</description>

Generated with cgit - Back to sebastiano.tronto.net