sebastiano.tronto.net

Source files and build scripts for my personal website
git clone https://git.tronto.net/sebastiano.tronto.net
Download | Log | Files | Refs | README

ed.md (12852B)


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