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