aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2022-09-26 11:45:12 +0200
committerSebastiano Tronto <sebastiano@tronto.net>2022-09-26 11:45:12 +0200
commitd328796d7faf4ec7ad671f0996c4ea7d3cabfe45 (patch)
tree192d113329f3c690add957532a2f0425c3cd67b7 /src
parent1e4e1bb0ec89f63bca7180fdb0e37031915dea15 (diff)
downloadsebastiano.tronto.net-d328796d7faf4ec7ad671f0996c4ea7d3cabfe45.tar.gz
sebastiano.tronto.net-d328796d7faf4ec7ad671f0996c4ea7d3cabfe45.zip
Added blog post
Diffstat (limited to 'src')
-rw-r--r--src/blog/2022-09-20-sh-2/sh-2.md394
-rw-r--r--src/blog/blog.md1
-rw-r--r--src/blog/feed.xml7
3 files changed, 402 insertions, 0 deletions
diff --git a/src/blog/2022-09-20-sh-2/sh-2.md b/src/blog/2022-09-20-sh-2/sh-2.md
new file mode 100644
index 0000000..7f93d5f
--- /dev/null
+++ b/src/blog/2022-09-20-sh-2/sh-2.md
@@ -0,0 +1,394 @@
1# The man page reading club: sh(1) - part 2: commands and builtins
2
3This is the second and last part of our exciting sh(1) manual page
4read. This time we are going to learn about *commands* and *builtins*.
5In case you have missed it, check out the [first part](../2022-09-13-sh-1)
6where we dealt with the shell's grammar.
7
8I'll spare you the fan fiction this time - let's go straight to the
9technical part!
10
11As usual, you can follow along at
12[man.openbsd.org](https://man.openbsd.org/OpenBSD-7.1/sh)
13
14## Commands
15
16The Commands section of the manual page starts like this:
17
18```
19 The shell first expands any words that are not variable assignments or
20 redirections, with the first field being the command name and any
21 successive fields arguments to that command. It sets up redirections, if
22 any, and then expands variable assignments, if any. It then attempts to
23 run the command.
24```
25
26The next few paragraphs describe how the name of a command is
27interpreted. There are two distinct cases: if the name contains
28any slashes, it is considered as a path to a file; if it does not,
29the shell tries to interpret it as a special builtin, as a shell
30function, as a non-special builtin (the difference between these
31two types of builtins will be explained later) or finally as the
32name of an executable file (binary or script) to be looked for in
33`$PATH`.
34
35The meaning of this variable is explained in the `ENVIRONMENT`
36section:
37
38```
39PATH Pathname to a colon separated list of directories used to search for
40 the location of executable files. A pathname of `.' represents the
41 current working directory. The default value of PATH on OpenBSD is:
42
43 /usr/bin:/bin:/usr/sbin:/sbin:/usr/X11R6/bin:/usr/local/bin
44```
45
46### Grouping commands
47
48The manual page continues with explaining how to group commands
49together to create more complex commands. There are five ways to
50create a list of commands, and their syntax is always of the form
51
52```
53 command SEP command SEP ...
54```
55
56where `SEP` is one of the separators described below.
57
58* *Sequential lists*: One or more commands separated by a semicolon `;`
59 are exectuted in order one after the other.
60* *Asynchronous lists*: One or more commands separated by an ampersand `&`
61 are executed in parallel, each in a different subshell.
62* *Pipelines*: Two or more commands separated by a pipe `|` are executed
63 in order, using the output of each command as input for the next one.
64 Together with I/O redirection, that we have seen last time, pipelines are
65 one of the "killer features" of UNIX that makes its shell such a powerful
66 language that it is still widely appreciated more than fifty years after
67 its introduction.
68* *AND lists*: Two or more commands separated by a double ampersand `&&`
69 are executed in order, but a command is only run if the exit status of
70 the previous command was zero.
71* *OR lists*: Two or more commands separated by a double pipe `||`
72 are executed in order, but a command is only run if the exit status of
73 the previous command was different from zero.
74
75The AND and OR lists can be combined by using a mix of `&&` and
76`||`. The two operators have the same precedence.
77
78The exit status of a list of commands is equal to the exit status
79of the last commands executed, except for asynchronous lists where
80the exit status is always zero. For pipelines, the exit status can
81be inverted by putting an exclamation mark `!` at the beginning of
82the list.
83
84Now that I think about it, I have mentioned the exit status of a
85command a few times here and in the last episode, but I have never
86explained what it is. Basically, every command concludes its
87execution by returning a number (exit status), which may be zero
88to indicate a succesful execution or anything different from zero
89to indicate a failure. This will become even more relevant soon.
90
91Finally, a list of commands can be treated as a single command by
92enclosing it in parentheses or in braces:
93
94```
95 Command lists, as described above, can be enclosed within `()' to have
96 them executed in a subshell, or within `{}' to have them executed in the
97 current environment:
98
99 (command ...)
100 { command ...; }
101
102 Any redirections specified after the closing bracket apply to all
103 commands within the brackets. An operator such as `;' or a newline are
104 needed to terminate a command list within curly braces.
105```
106
107### Flow control
108
109Much like any imperative programming language, the shell has some
110constructs that allow controlling the flow of the execution. The
111*for loop* is perhaps the most peculiar one. Its format is:
112
113```
114 for name [in [word ...]]
115 do
116 command
117 ...
118 done
119```
120
121The commands are executed once for every item in the expansion of
122`[word ...]` and every time the value of the variable `name` is set
123to one of these items. (check [the last episode](../2022-09-13-sh-1)
124for an explanation of text expansion).
125
126*While loops* are perhaps more familiar to regular programmers: a
127command called *condition* is run, and if its exit code is zero the
128body of the while loop is executed, and so on. The format is
129
130```
131 while condition
132 do
133 command
134 ...
135 done
136```
137
138There is an opposite construct with `until` in place of `while`
139which executes the body as long as `condition` exits with non-zero
140status.
141
142A *case conditional* can be used to run commands depending on
143something matching a pattern. The format is
144
145```
146 case word in
147 (pattern [| pattern ...]) command;;
148 ...
149 esac
150```
151
152Where `pattern` can be expressed using the usual filename globbing
153syntax that we briefly covered last time - see
154[glob(7)](https://man.openbsd.org/OpenBSD-7.1/glob.7) for more
155details.
156
157As an example, this short code snippet tries to determine the type
158of the file given as first argument from its extension:
159
160```
161case "$1" in
162 (*.txt) echo "Text file";;
163 (*.wav | *.mp3 | *.ogg) echo "Music file";;
164 (*) echo "Something else";;
165esac
166```
167
168Note that double quotes around the `$1` to avoid file names with
169spaces being considered as multiple words.
170
171The *if conditional* is also a classic construct that programmers
172are very familiar with. Its general format is
173
174```
175 if conditional
176 then
177 command
178 ...
179 elif conditional
180 then
181 command
182 ...
183 else
184 command
185 ...
186 fi
187```
188
189Like for the `while` construct, `conditional` is a command that is
190run and its exit status is evaluated. `elif` is just short for
191"else, if...".
192
193Finally, the shell also has functions, that are basically groups
194of commands that can be given a name and executed when using that
195name as a command. Their syntax may be simpler than you expect:
196
197```
198 function() command-list
199```
200
201When defining functions it is common to write `command-list` in the
202`{ command ; command ; ... ; }` format. Replacing the semicolons
203with newlines we get the more familiar-looking structure
204
205```
206 function() {
207 command
208 command
209 ...
210 }
211```
212
213## Builtins
214
215The builtins are listed in alphabetic order in the manual page,
216which is very convenient when consulting it for reference, but it
217is not the best choice for a top-to-bottom read. So I'll shuffle
218them around and divide them into a few groups. I'll skip some stuff,
219but I'll try to cover what is important for regular use.
220
221But first, as promised at the beginning of the previous section,
222we need to explain the difference between "special" and regular
223builtins.
224
225```
226 A number of built-ins are special in that a syntax error can cause a
227 running shell to abort, and, after the built-in completes, variable
228 assignments remain in the current environment. The following built-ins
229 are special: ., :, break, continue, eval, exec, exit, export, readonly,
230 return, set, shift, times, trap, and unset.
231```
232
233### More programming features
234
235As we have seen, the shell language includes some classical programming
236constructs, like `if` and `while`. There are more builtins that can be
237helpful these constructs: for example `true` and `false` are builtins
238that do nothing and return a zero and a non-zero value respectively,
239thus acting as sort of "boolean variables".
240
241The builtins `break` and `continue`, used inside a loop of any kind,
242behave exactly as in C. The builtin `return` is used to exit the current
243function. An exit code may be specified as a parameter, to indicate
244success (0) or failure (any other number).
245
246### Variables
247
248The builtin `read` can be used to get input from the user - or
249indeed from anywhere else, thanks to redirection:
250
251```
252read [-r] name ...
253 Read a line from standard input. The line is split into fields, with
254 each field assigned to a variable, name, in turn (first field
255 assigned to first variable, and so on). If there are more fields
256 than variables, the last variable will contain all the remaining
257 fields. If there are more variables than fields, the remaining
258 variables are set to empty strings. A backslash in the input line
259 causes the shell to prompt for further input.
260
261 The options to the read command are as follows:
262
263 -r Ignore backslash sequences.
264```
265
266As an example of reading from something other than standard input,
267this short script takes a filename as an argument and prints each
268line of the file preceded by its line number:
269
270```
271i=0
272while read line
273do
274 i=$((i+1))
275 echo $i: $line
276done < $1
277```
278
279Notice that the redirector `< $1` is placed at the end of the `while`
280commend, after then closing `done`.
281
282The builtins `export` and `readonly` deal with permissions: the
283first is used to make a variable visible to all subsequently ran
284commands (by default it is not), while the latter is used to make
285a variable unchangeable. The syntax is the same for both:
286
287```
288 command [-p] name[=value]
289```
290
291If `=value` is given, the value is assigned to the variable before
292changing the permissions. The option `-p` is used to list out all
293the variables that are currently exported or set as read-only.
294
295### Running commands
296
297If you want to run the commands contained in `file`, you can do so
298by using `. file` (the single dot is a builtin). For example you
299can list some commands that you want to run at the beginning of
300each shell session (e.g. aliases, see the next section) and run
301them with just one command. Many other shells, such as ksh, run
302certain files like `.profile` at startup, but sh does not.
303
304If the commands you want to run are saved in variables or other
305parameters you can use `eval`. For example, the following script
306takes a command and its arguments as parameters, runs them and
307returns a different message depending on the exit code:
308
309```
310if eval $@
311then
312 echo "The command $@ ran happily"
313else
314 echo "Oh no! Something went wrong!"
315fi
316```
317
318### Aliases
319
320Aliases provide a nice shortcut sometimes, for example for shortening
321a long command name or for adding a certain set of options by
322default.
323
324Using `alias name=value` makes it so every time `name` is read by
325the shell as a command (i.e. not when it is an argument) it is
326replaced by `value`. For example using `alias off='shutdown -p now'`
327can be used to easily call the `shutdown` command with the common
328option `-p now` - check out [an older blog entry](../2022-07-07-shutdown)
329to learn about this surprisingly feature-rich command!
330
331Using just `alias name` tells you the value of the corresponding alias,
332if it is set. Using `alias` with no argument returns a list of all
333currently set aliases. Contrary to variables, aliases are visible in
334every subshell.
335
336Finally, `unalias name` can be used to unset the corresponding
337alias; `unalias -a` unsets all currently set aliases.
338
339### Moving around directories
340
341Next (a meaningless word, since we are going in our own completely
342arbitrary order) we have `cd` and `pwd`, which can be used to move around
343in the directory tree.
344
345`pwd` simply prints the current path - it is short for "Print Working
346Directory". The working directory is where files are looked for by
347the shell, for example when used as arguments for commands. If a
348file is not in the current working directory, its full path has to
349be specified in order to refer to it.
350
351The working directory can be changed with `cd path/to/new/directory`.
352If the path is not specified, it defaults to `$HOME`, the home
353directory of the current user. The path can also be a single dash
354`-`, meaning "return to the previous working directory". Finally,
355if the path does not start with a slash and is not found relatively
356to the current working directory, the variable `CDPATH`, which
357should contain a colon-separated list of directories, is read to
358try and find the new directory starting from there.
359
360### Jobs
361
362The builtins `jobs`, `kill`, `bg` and `fg` can be used to manage multiple
363jobs running in the same shell. For example you can can run a command in
364the background with `command &`, and later kill it with `kill [id]` or
365bring it to the foreground with `fg [id]` (the `id` of the command will
366be printed by the shell when you run `command &`).
367
368I wanted to write something more about this, but I found the man
369page for sh a bit lacking. I had to rely on other resources, such
370as the manual page of [ksh(1)](https://man.openbsd.org/OpenBSD-7.1/ksh).
371I think I'll postpone *job control* to another entry. Stay tuned!
372
373### And finally...
374
375```
376exit [n]
377 Exit the shell with exit status n, or that of the last command executed.
378```
379
380## Conclusion
381
382I have skipped a few sections of the man page and many of the
383builtins, but I am happy with the result and I think we can end it
384here. After all, if I did not make any selection at all for these
385"reading club" entries, you could just read the manual page yourself,
386so what would the point be?
387
388I am not sure what I am going to cover in the next episode. On the one
389hand I should alternate between shorter pages and longer ones, mainly
390to avoid burning out by taking on too many huge projects. But on the
391other hand long pages are often more interesting.
392
393Anyway, I hope you enjoyed this long double-post and that you may have
394learnt something new. See you next time!
diff --git a/src/blog/blog.md b/src/blog/blog.md
index 2a02644..a1227f7 100644
--- a/src/blog/blog.md
+++ b/src/blog/blog.md
@@ -2,6 +2,7 @@
2 2
3[RSS Feed](feed.xml) 3[RSS Feed](feed.xml)
4 4
5* 2022-09-20 [The man page reading club: sh(1) - part 2: commands and builtins](2022-09-20-sh-2)
5* 2022-09-13 [The man page reading club: sh(1) - part 1: shell grammar](2022-09-13-sh-1) 6* 2022-09-13 [The man page reading club: sh(1) - part 1: shell grammar](2022-09-13-sh-1)
6* 2022-09-10 [Long live netbooks!](2022-09-10-netbooks) 7* 2022-09-10 [Long live netbooks!](2022-09-10-netbooks)
7* 2022-09-05 [Pipe man into col -b to get rid of \^H](2022-09-05-man-col) 8* 2022-09-05 [Pipe man into col -b to get rid of \^H](2022-09-05-man-col)
diff --git a/src/blog/feed.xml b/src/blog/feed.xml
index 3b718d2..d8e7876 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>The man page reading club: sh(1) - part 2: commands and builtins</title>
13<link>https://sebastiano.tronto.net/blog/2022-09-20-sh-2</link>
14<description>The man page reading club: sh(1) - part 2: commands and builtins</description>
15<pubDate>2022-09-20</pubDate>
16</item>
17
18<item>
12<title>The man page reading club: sh(1) - part 1: shell grammar</title> 19<title>The man page reading club: sh(1) - part 1: shell grammar</title>
13<link>https://sebastiano.tronto.net/blog/2022-09-13-sh-1</link> 20<link>https://sebastiano.tronto.net/blog/2022-09-13-sh-1</link>
14<description>The man page reading club: sh(1) - part 1: shell grammar</description> 21<description>The man page reading club: sh(1) - part 1: shell grammar</description>

Generated with cgit - Back to sebastiano.tronto.net