diff options
Diffstat (limited to 'src/blog')
| -rw-r--r-- | src/blog/2022-12-24-ed/ed.md | 421 | ||||
| -rw-r--r-- | src/blog/blog.md | 1 | ||||
| -rw-r--r-- | src/blog/feed.xml | 7 |
3 files changed, 429 insertions, 0 deletions
diff --git a/src/blog/2022-12-24-ed/ed.md b/src/blog/2022-12-24-ed/ed.md new file mode 100644 index 0000000..5cd9701 --- /dev/null +++ b/src/blog/2022-12-24-ed/ed.md | |||
| @@ -0,0 +1,421 @@ | |||
| 1 | # The man page reading club: ed(1) | ||
| 2 | |||
| 3 | I enjoyed writing a little introduction at the beginning of every | ||
| 4 | post of this series, but I am running out of ideas. I am not much | ||
| 5 | of fiction writer. I'll skip this time, maybe I'll get back to doing | ||
| 6 | it in the future. | ||
| 7 | |||
| 8 | For this episode I chose to explore ed, | ||
| 9 | [the standard editor](https://www.gnu.org/fun/jokes/ed-msg.en.html). | ||
| 10 | This little piece of software first appeared in the very first | ||
| 11 | version of UNIX, in the late '60s. Back then, the most common way | ||
| 12 | to interact with a computer was via a | ||
| 13 | [teletype](https://en.wikipedia.org/wiki/Teleprinter). This meant | ||
| 14 | that, in order to edit a file, you could not simply show a screenful | ||
| 15 | of text and modify it interactively. | ||
| 16 | |||
| 17 | As we will see in this post, the way you modify your text files | ||
| 18 | with ed is by running commands, as you would in your usual | ||
| 19 | [shell](../2022-09-13-sh-1). You might wonder why in the world you | ||
| 20 | should be interested in using this over a more human-friendly text | ||
| 21 | editor. There are at least a couple of reasons: | ||
| 22 | |||
| 23 | 1. You find yourself in a very limited environment (e.g. some ebedded | ||
| 24 | OS) where ed is the only editor available. | ||
| 25 | 2. You want to edit a text file as part of a shell script - you can | ||
| 26 | find an example in my [last blog entry](../2022-11-23-git-host). | ||
| 27 | |||
| 28 | ## ed(1) | ||
| 29 | |||
| 30 | *Follow along at | ||
| 31 | [man.openbsd.org](http://man.openbsd.org/OpenBSD-7.2/ed)* | ||
| 32 | |||
| 33 | The first section explains a few fundamental things. | ||
| 34 | |||
| 35 | First of all, ed can be invoked with a file as an argument. The | ||
| 36 | given file is copied into a *buffer*, and changes are written to | ||
| 37 | it only when the user issues a `w` command. | ||
| 38 | |||
| 39 | ed reads every line the user inputs and tries to interpret it as a | ||
| 40 | command. The general form for an ed command is | ||
| 41 | |||
| 42 | ``` | ||
| 43 | [address[,address]]command[parameters] | ||
| 44 | ``` | ||
| 45 | |||
| 46 | Where the (optional) addresses specify a range of lines over which | ||
| 47 | the command has to operate. See the **Line addressing** section | ||
| 48 | below for more info. | ||
| 49 | |||
| 50 | Some commands allow you to switch to *input mode*, where ed reads | ||
| 51 | text without trying to interpret it as a command - usually for the | ||
| 52 | purpose of inserting it in your file - until a line with a single | ||
| 53 | dot `.` character is read. | ||
| 54 | |||
| 55 | There are only two command line options for ed: `-s` to suppress | ||
| 56 | diagnostic messages and `-p prompt` to specify a prompt string for | ||
| 57 | its command line. | ||
| 58 | |||
| 59 | ### Line addressing | ||
| 60 | |||
| 61 | Every command operates on one or more lines. The default is the | ||
| 62 | *current line*, which is usually set to be the last line affected | ||
| 63 | by the last command. For example, after opening a file, the current | ||
| 64 | line is set to its last line. | ||
| 65 | |||
| 66 | You can specify the line(s) on which a command shall operate by | ||
| 67 | prepending one or two *addresses* separated by a comma or a semicolon. | ||
| 68 | The difference is the following: | ||
| 69 | |||
| 70 | ``` | ||
| 71 | Each address in a comma-delimited range is interpreted relative to the | ||
| 72 | current address. In a semi-colon-delimited range, the first address is | ||
| 73 | used to set the current address, and the second address is interpreted | ||
| 74 | relative to the first. | ||
| 75 | ``` | ||
| 76 | |||
| 77 | For example, let's say you are on line 3. Then the address range | ||
| 78 | `5,+4` selects the lines from 5 to 7 (3+4), while `5;+4` selects 5 | ||
| 79 | to 9 (5+4). | ||
| 80 | |||
| 81 | But what is a valid address, actually? Besides simply specifying a | ||
| 82 | line number, there are a variety of ways to address line: For | ||
| 83 | example, `.` refers to the current line and `$` to the last line | ||
| 84 | of the file. As we have seen, you can also specify relative addresses | ||
| 85 | in the form `-n` or `+n`. You can also search for a line containing | ||
| 86 | a specific patern: | ||
| 87 | |||
| 88 | ``` | ||
| 89 | /re/ The next line containing the regular expression re. The search | ||
| 90 | wraps to the beginning of the buffer and continues down to the | ||
| 91 | current line, if necessary. The second slash can be omitted if | ||
| 92 | it ends a line. "//" repeats the last search. | ||
| 93 | ``` | ||
| 94 | |||
| 95 | For more infor about regular expressions, see | ||
| 96 | [re_format(7)](http://man.openbsd.org/re_format) | ||
| 97 | (or perhaps my next blog entry?). Using question marks instead of | ||
| 98 | slashes (like this: `?re?`) searches backwards. | ||
| 99 | |||
| 100 | ### Commands | ||
| 101 | |||
| 102 | ed offers a lot of commands to manipulate text. If you are familiar | ||
| 103 | with other UNIX editors such as vi or sed, you may recognize many | ||
| 104 | of them. | ||
| 105 | |||
| 106 | As usual, they are listed in alphabetic order in the manual page, | ||
| 107 | so I took the liberty of re-arranging them into groups. I'll have to | ||
| 108 | skip or just briefly mention some of them, otherwise I might just | ||
| 109 | as well copy the whole manual page here. | ||
| 110 | |||
| 111 | **Address-only commands** | ||
| 112 | |||
| 113 | Specifying an address only, without a command, changes the current | ||
| 114 | line to the (last) addressed line and it prints it. The default is | ||
| 115 | the next line, i.e. if you just press enter ed prints out the next | ||
| 116 | line and sets it as the current line. | ||
| 117 | |||
| 118 | **Printing lines** | ||
| 119 | |||
| 120 | The command `p` prints the addressed line. The command `n` does the | ||
| 121 | same, but it also prints line numbers. `l` is the same as `p`, but | ||
| 122 | special characters (e.g. new lines) are made visible. | ||
| 123 | |||
| 124 | Most commands accept a *print suffix* `p`, `l` or `n` that instructs | ||
| 125 | ed to print the last line affected by the command. Thus, the three | ||
| 126 | printing commands can be also seen as an *address-only command* | ||
| 127 | followed by a print suffix. | ||
| 128 | |||
| 129 | **Basic editing** | ||
| 130 | |||
| 131 | The commands `a` and `i` toggle *input mode* to let you insert text | ||
| 132 | after (**a**ppend) or before (**i**nsert) the last line addressed. | ||
| 133 | Usually you want to address a single line, often the current line, | ||
| 134 | when using one of these commands. | ||
| 135 | |||
| 136 | The commands `d` and `c` can be used to **d**elete or **c**hange | ||
| 137 | the addressed lines. The latteris equivalent to `d` followed by an | ||
| 138 | `a` command. | ||
| 139 | |||
| 140 | **Copying, moving and joining lines.** | ||
| 141 | |||
| 142 | The commands `t` and `m` operate on a range and take an extra single | ||
| 143 | address (which can be `0`) as a parameter, and copy (**t**ransfer) | ||
| 144 | or **m**ove the addressed lines to that location. For example the | ||
| 145 | command `2,4t0` will copy the 2nd, 3rd and 4th lines to the beginning | ||
| 146 | of the file. | ||
| 147 | |||
| 148 | If you want to join multiple lines in one you can use the `j` command. | ||
| 149 | |||
| 150 | **Text substitution** | ||
| 151 | |||
| 152 | The `s` command is one of the most powerful ed offers, but also one | ||
| 153 | of the most complex. It allows you to replace a piece of text, or | ||
| 154 | any arbitrary pattern defined by a regular expression, with whatever | ||
| 155 | you like. | ||
| 156 | |||
| 157 | It comes in three variants: | ||
| 158 | |||
| 159 | ``` | ||
| 160 | (.,.)s/pattern/text/ | ||
| 161 | (.,.)s/pattern/text/g | ||
| 162 | (.,.)s/pattern/text/n | ||
| 163 | ``` | ||
| 164 | |||
| 165 | Where `pattern` is a regular expression and `text` is simple text. | ||
| 166 | The first form replaces only the first occurrence of `pattern` in | ||
| 167 | each selected line, while the second replaces every occurrence. In | ||
| 168 | the last form, `n` must be a number, and only the n-th occurrence | ||
| 169 | is replaced. | ||
| 170 | |||
| 171 | There are some special characters that can be used: for example, a | ||
| 172 | single `&` in `text` is equivalent to the currently matched text. | ||
| 173 | If `text` consists of a single `%`, the `text` argument of the last | ||
| 174 | `s` command issued is used. | ||
| 175 | |||
| 176 | You may escape any character in `text`, including newlines, by | ||
| 177 | prepending a backaslash. To avoid escaping slashes to death, keep | ||
| 178 | in mind that you can use any other character, for example a `|`, | ||
| 179 | instead of `/` in the `s` command. | ||
| 180 | |||
| 181 | Finally, a simple `s` command, without pattern or text, repeats the | ||
| 182 | last substitution issued. | ||
| 183 | |||
| 184 | Let's put this all together with a single example. We have a file | ||
| 185 | that looks like this: | ||
| 186 | |||
| 187 | ``` | ||
| 188 | This is the first line | ||
| 189 | Another line, called the second line | ||
| 190 | /A third line, with boundaries/ | ||
| 191 | Let's make it four | ||
| 192 | ``` | ||
| 193 | |||
| 194 | And run the following ed commands: | ||
| 195 | |||
| 196 | ``` | ||
| 197 | 1/s/t/T/ | ||
| 198 | 1/s/T/&&/g | ||
| 199 | 2/s/l/%/ | ||
| 200 | 1,3s | ||
| 201 | 3s|/|\||g | ||
| 202 | ``` | ||
| 203 | |||
| 204 | The result is: | ||
| 205 | |||
| 206 | ``` | ||
| 207 | TThis is TThe first lline | ||
| 208 | Another llline, called the second line | ||
| 209 | |A third lline, with boundaries| | ||
| 210 | Let's make it four | ||
| 211 | ``` | ||
| 212 | |||
| 213 | Understanding why you get this is left as an exercise for the reader ;-) | ||
| 214 | |||
| 215 | **Multiple commands on selected lines** | ||
| 216 | |||
| 217 | The commands `g` and `G` are also quite powerful. With | ||
| 218 | |||
| 219 | ``` | ||
| 220 | (.,.)g/pattern/command-list | ||
| 221 | ``` | ||
| 222 | |||
| 223 | you can specify a list of commands to be executed on every line | ||
| 224 | matching the regular expression `pattern`. The commands in the list | ||
| 225 | are each on their own line, ended with a backslash. The command `G` | ||
| 226 | is essentially an interactive version of `g`. Check the man page | ||
| 227 | for more details! | ||
| 228 | |||
| 229 | **Marks** | ||
| 230 | |||
| 231 | You can mark a line with a single lowercase letter (say x) using | ||
| 232 | the command `[address]kx`. What for, you might ask? Well, when | ||
| 233 | talking about addresses I omitted to tell you that you can also | ||
| 234 | refer to a marked line using `'x`. You only have 26 marks at your | ||
| 235 | disposal, and one is only deleted when the line it marks is modified, | ||
| 236 | so use them wisely! | ||
| 237 | |||
| 238 | **Reading files and commands** | ||
| 239 | |||
| 240 | You can use the `r` command to insert the content of a file (with | ||
| 241 | `r filename`) or the output of a command (with `r !command`) after | ||
| 242 | the current line. This is the same as the `r` command of vi, which | ||
| 243 | I have discussed in a [previous blog entry](../2022-09-05-man-col). | ||
| 244 | |||
| 245 | **Undo** | ||
| 246 | |||
| 247 | Use the `u` command to undo the last command. Using `u` twice | ||
| 248 | undoes the undo. No editing history for you, sorry. | ||
| 249 | |||
| 250 | **File management** | ||
| 251 | |||
| 252 | From inside ed, you can use `e filename` to open a new file (**e**dit), | ||
| 253 | `w` to save your changes to the current file (**w**rite) and `q` | ||
| 254 | to quit. These commands have an upper-case variant (`E`, `W`, and | ||
| 255 | `Q`) that can be used to ignore errors (e.g. quit without saving). | ||
| 256 | |||
| 257 | The command `wq` can be used as a shortcut for saving and closing. | ||
| 258 | |||
| 259 | ### ? | ||
| 260 | |||
| 261 | ed is infamously terse with its error messages. Indeed, whatever | ||
| 262 | error you make, you are going to be faced with the following | ||
| 263 | informative line: | ||
| 264 | |||
| 265 | ``` | ||
| 266 | ? | ||
| 267 | ``` | ||
| 268 | |||
| 269 | But don't worry: the command `h` shows a more verbose description | ||
| 270 | of the last error. You can use the `H` command to toggle verbose | ||
| 271 | error messages for the whole session. | ||
| 272 | |||
| 273 | ## An example session | ||
| 274 | |||
| 275 | Let's write an *Hello world* text file using ed! | ||
| 276 | |||
| 277 | Let's start by calling ed with a reasonable prompt, to make our | ||
| 278 | life easier. | ||
| 279 | |||
| 280 | ``` | ||
| 281 | $ ed -p 'ed > ' hellow.txt | ||
| 282 | ``` | ||
| 283 | |||
| 284 | And let's open a (new) file: | ||
| 285 | |||
| 286 | ``` | ||
| 287 | ed > e hellow.txt | ||
| 288 | ``` | ||
| 289 | |||
| 290 | Don't worry about the (unusually verbose!) error message. The file | ||
| 291 | does not exist yet, but it will be created when we save our work | ||
| 292 | with `w`. Now let's add a line of text: | ||
| 293 | |||
| 294 | ``` | ||
| 295 | ed > a | ||
| 296 | Hello, wolrd! | ||
| 297 | wq | ||
| 298 | |||
| 299 | ``` | ||
| 300 | |||
| 301 | Wait, why is ed still open? And why is it not showing the `ed > ` | ||
| 302 | prompt? Oh right, we forgot to end the input mode by entering a | ||
| 303 | single dot! | ||
| 304 | |||
| 305 | ``` | ||
| 306 | . | ||
| 307 | ed > | ||
| 308 | ``` | ||
| 309 | |||
| 310 | Ok, now we are back in business. But we have to remove the `wq` | ||
| 311 | line we entered by mistake: | ||
| 312 | |||
| 313 | ``` | ||
| 314 | ed > /wq/d | ||
| 315 | ``` | ||
| 316 | |||
| 317 | Let's check that we have written what we intended to by printing | ||
| 318 | the content of the file: | ||
| 319 | |||
| 320 | ``` | ||
| 321 | ed > 1,$n | ||
| 322 | 1 Hello, wolrd! | ||
| 323 | ``` | ||
| 324 | |||
| 325 | Oh no, there is a typo! No big deal, we can fix it: | ||
| 326 | |||
| 327 | ``` | ||
| 328 | ed > 1s/lr/rl/ | ||
| 329 | ``` | ||
| 330 | |||
| 331 | And now that we are done, we can close our file: | ||
| 332 | |||
| 333 | ``` | ||
| 334 | ed > q | ||
| 335 | ? | ||
| 336 | ``` | ||
| 337 | |||
| 338 | Wait, what's going on? Let's check: | ||
| 339 | |||
| 340 | ``` | ||
| 341 | ed > h | ||
| 342 | warning: file modified | ||
| 343 | ``` | ||
| 344 | |||
| 345 | Oh right, we need to save. | ||
| 346 | |||
| 347 | ``` | ||
| 348 | ed > wq | ||
| 349 | ``` | ||
| 350 | |||
| 351 | And now we are done! | ||
| 352 | |||
| 353 | ## I/O redirection magic | ||
| 354 | |||
| 355 | As all other basic UNIX utilities, ed can be used non-interactively | ||
| 356 | by using input / output redirection. As an example, consider the | ||
| 357 | interactive session above. The input we fed to ed was: | ||
| 358 | |||
| 359 | ``` | ||
| 360 | e hellow.txt | ||
| 361 | a | ||
| 362 | Hello, wolrd! | ||
| 363 | wq | ||
| 364 | . | ||
| 365 | /wq/d | ||
| 366 | 1,$n | ||
| 367 | 1s/lr/rl/ | ||
| 368 | q | ||
| 369 | h | ||
| 370 | wq | ||
| 371 | ``` | ||
| 372 | |||
| 373 | If we save (a stripped down version of) the text above in a file | ||
| 374 | called `edcommands.txt` | ||
| 375 | |||
| 376 | ``` | ||
| 377 | a | ||
| 378 | Hello, wolrd! | ||
| 379 | wq | ||
| 380 | . | ||
| 381 | /wq/d | ||
| 382 | 1s/lr/rl/ | ||
| 383 | wq | ||
| 384 | ``` | ||
| 385 | |||
| 386 | and run | ||
| 387 | |||
| 388 | ``` | ||
| 389 | $ ed -s hellow2.txt < edcommands.txt | ||
| 390 | ``` | ||
| 391 | |||
| 392 | We should obtain a file `hellow2.txt` identical to `hellow.txt`. I | ||
| 393 | say "should" because apparently there is a little caveat: when used | ||
| 394 | non-interactively, ed exits on the first error it encounters. This | ||
| 395 | also happens with the `No such file or directory` error that we get | ||
| 396 | at the beginning, if a file `hellow2.txt` does not exist yet. We | ||
| 397 | just have to create one in advance, for example with `touch | ||
| 398 | hellow2.txt`, and run again the ed command above. | ||
| 399 | |||
| 400 | ## Conclusions | ||
| 401 | |||
| 402 | ed was designed in a time when the computer-human interaction was | ||
| 403 | a bit different from now, and it shows. However, its language is | ||
| 404 | pleasantly consistent: every action you want to perform is expressed | ||
| 405 | in the address-command-parameters form. This makes it easy to learn | ||
| 406 | and boring, which is a good thing. Such consistency is much harder | ||
| 407 | to achieve in the 2D graphical world - which includes | ||
| 408 | [TUIs](https://en.wikipedia.org/wiki/Text-based_user_interface). | ||
| 409 | |||
| 410 | At the beginning of the post I have mentioned two use cases for a | ||
| 411 | software like ed in the present day: being forced into a limited | ||
| 412 | environment and using it in non-interactive mode. But there is at | ||
| 413 | least another one: for visually impaired users, modern computer | ||
| 414 | interfaces are largely inaccessible, as they can't look at a wall | ||
| 415 | of text and pictures to figure out where the stuff they want to | ||
| 416 | work on is. On the other hand, an editor like ed does not overwhelm | ||
| 417 | users with visual output and does not require them to keep in mind | ||
| 418 | more than one line at the time. If you are interest in this topic | ||
| 419 | I highly suggest reading the article | ||
| 420 | [The command line philosophy](http://www.eklhad.net/philosophy.html) | ||
| 421 | by Karl Dahlke, the author of [edbrowse](https://edbrowse.org). | ||
diff --git a/src/blog/blog.md b/src/blog/blog.md index 3ffa250..514492d 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-12-24 [The man page reading club: ed(1)](2022-12-24-ed) | ||
| 5 | * 2022-11-23 [Self-hosted git pages with stagit (featuring ed, the standard editor)](2022-11-23-git-host) | 6 | * 2022-11-23 [Self-hosted git pages with stagit (featuring ed, the standard editor)](2022-11-23-git-host) |
| 6 | * 2022-10-19 [Keeping my email sorted (the hard way)](2022-10-19-email-setup) | 7 | * 2022-10-19 [Keeping my email sorted (the hard way)](2022-10-19-email-setup) |
| 7 | * 2022-10-01 [The man page reading club: tetris(6)](2022-10-01-tetris) | 8 | * 2022-10-01 [The man page reading club: tetris(6)](2022-10-01-tetris) |
diff --git a/src/blog/feed.xml b/src/blog/feed.xml index 0efe9ec..e84456a 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: ed(1)</title> | ||
| 13 | <link>https://sebastiano.tronto.net/blog/2022-12-24-ed</link> | ||
| 14 | <description>The man page reading club: ed(1)</description> | ||
| 15 | <pubDate>2022-12-24</pubDate> | ||
| 16 | </item> | ||
| 17 | |||
| 18 | <item> | ||
| 12 | <title>Self-hosted git pages with stagit (featuring ed, the standard editor)</title> | 19 | <title>Self-hosted git pages with stagit (featuring ed, the standard editor)</title> |
| 13 | <link>https://sebastiano.tronto.net/blog/2022-11-23-git-host</link> | 20 | <link>https://sebastiano.tronto.net/blog/2022-11-23-git-host</link> |
| 14 | <description>Self-hosted git pages with stagit (featuring ed, the standard editor)</description> | 21 | <description>Self-hosted git pages with stagit (featuring ed, the standard editor)</description> |
