Actually using ed

The classic ed editor is a really good example of a sparse, minimal, standard Unix tool that does one thing, and does it well. Because there are so many good screen-oriented editors for Unix, there’s seldom very much call for using ed, unless you’re working on very old or very limited hardware that won’t run anything else.

However, if part of the reason you use vi is because you think it will always be there (it may not be), then you should learn ed too. If you’re using any Unix at all, then ed really will always be there, no matter how old or limited the system. Well, unless you’re using Arch Linux. If your terminal is broken and vi won’t work, or you break it some other way, your choices may well be between cat and ed.

Not a friendly editor

Even more than its uppity grandchild ex/vi, ed has developed a reputation as terse and intimidating to newcomers. When you type ed at the command line, nothing happens, and the only error message presented by default is ?. If you’re reading this, it’s likely your first and only experience with ed went something like this:

$ ed
help
?
h
Invalid command suffix
?
?
^C
?
exit
?
quit
?
^Z
$ killall ed
$ vi

So, ed is not a terribly intuitive editor. However, it’s not nearly as hard to learn as it might seem, especially if you’re a veteran vi user and thereby comfortable with the ex command set. With a little practice, you can actually get rather quick with it; there’s an elegance to its almost brutal simplicity.

It’s also very interesting to learn how ed works and how to use it, not just because it might very well be useful for you one day, but because it occupies an important position in the heritage of the sed stream editor, the ex line editor, the vi visual editor, the grep tool, and many other contexts.

Why is ed so terse?

When ed was developed, the usual method of accessing a Unix system was via a teletype device, on which it wouldn’t have been possible to use a screen-oriented editor like vi. Similarly, modems were slow, and memory was precious; using abbreviated commands and terse error messages made a lot of sense, because the user would otherwise be wasting a lot of time waiting for the terminal to react to commands, and didn’t have a whole lot of memory to throw around for anything besides the buffer of text itself.

Of course, this is almost a non-issue for most Unix-like systems nowadays, so one of the first things we’ll do is make ed a little bit less terse and more user-friendly.

Error messages

Start ed up the usual way:

$ ed

We’ll start by deliberately doing something wrong. Type b and press Enter:

b
?

There’s that tremendously unhelpful ? again. But if you press h, you can see what went wrong:

h
Unknown command

Of course, since it’s the future now, we can spare the terminal cycles to have ed print the error message for us every time. You can set this up by pressing H:

H
b
?
Unknown command

That’s a bit more useful, and should make things easier.

Quitting

You can quit ed with q. Go ahead and do that. If you had unsaved changes in a buffer, you could type Q to quit unconditionally. Repeating yourself works too:

q
?
Warning: buffer modified
q

Command prompt

Let’s invoke ed again, but this time we’ll use the -p option to specify a command prompt:

$ ed -p:
:

We’ll use that from now on, which will make things clearer both for interpreting this tutorial and for remembering whether we’re in command mode, or entering text. It might even be a good idea to alias it:

$ alias ed='ed -p:'

Basic text input

We’ll start by adding a couple of lines of text to the new buffer. When you start ed with no filename, it starts an empty buffer for you, much like vi does.

Because there are no lines at all at present, press a to start adding some at the editor’s current position:

:a
Start typing a few lines.
Anything you want, really.
Just go right ahead.
When you're done, just type a period by itself.
.

That’s added four new lines to the buffer, and left the editor on line 4. You can tell that’s the case because typing p for print, just by itself, prints the fourth line:

:p
When you're done, just type a period by itself.

A little more useful is n (or pn in some versions), which will show both the line number and the contents of the line:

:n
4       When you're done, just type a period by itself.

So just like in ex, the current line is the default for most commands. You can make this explicit by referring to the current line as .:

:.n
4       When you're done, just type a period by itself.

You can move to a line just by typing its number, which will also print it as a side effect:

:3
Just go right ahead.
:.n
3       Just go right ahead.

Pressing a like you did before will start inserting lines after the current line:

:a
Entering another line.
.
:n
4       Entering another line.

Pressing i will allow you to insert lines before the current line:

:i
A line before the current line.
.
:n
4       A line before the current line.

You can replace a line with c:

:c
I decided I like this line better.
.
:n
5       I decided I like this line better.

You can delete lines with d:

:6d

And join two or more lines together with j:

:1,2j

You can prepend an actual line number to any of these commands to move to that line before running the command on it:

:1c
Here's a replacement line.
.
:1n
1       Here's a replacement line.

For most of these commands, the last line to be changed will become the new current line.

Ranges

You can select the entire buffer with 1,$ or , for short (% works too):

:,p
Here's a replacement line.
Just go right ahead.
I decided I liked this line better.
Entering another line.

Or a limited range of specific lines:

:2,3p
Just go right ahead.
I decided I liked this line better.

These ranges can include a reference to the current line with .:

:2
Just go right ahead.
:.,4p
Just go right ahead.
I decided I liked this line better.
Entering another line.

They can also include relative line numbers, prefixed with + or -:

:2
:-1,+1p
Here's a replacement line.
Just go right ahead.
I decided I liked this line better.

You can drop a mark on a line with k followed by a lowercase letter such as a, and you’re then able to refer to it in ranges as 'a:

:3ka
:'ap
I decided I liked this line better.

Moving and copying

Move a line or range of lines to after a target line with m:

:1,2m$
:,p
I decided I liked this line better.
Entering another line.
Here's a replacement line.
Just go right ahead.

Copy lines to after a target line with t:

:2t4
:,p
I decided I liked this line better.
Entering another line.
Here's a replacement line.
Just go right ahead.
Entering another line.

Regular expressions

You can select lines based on classic regular expressions with the g operator. To print all lines matching the regular expression /re/:

:g/re/p
Here's a replacement line.

(Hmm, where have I seen that command before?)

You can invert the match to work with lines that don’t match the expression with v:

:v/re/p
I decided I liked this line better.
Entering another line.
Just go right ahead.
Entering another line.

Just like numbered line ranges, ranges selected with regular expressions can have other operations applied to them. To move every line containing the expression /re/ to the bottom of the file:

:g/re/m$
:,p
I decided I liked this line better.
Entering another line.
Just go right ahead.
Entering another line.
Here's a replacement line.

Searching

You can move to the next line after the current one matching a regular expression with /. Again, this will print the line’s contents as a side effect.

:/like
I decided I like this line better.

You can search backward with ?:

:?Here
Here's a replacement line.

Substituting

You can substitute for the first occurrence per line of an expression within a range of lines with the s command:

:1s/i/j
I decjded I like this line better.

You can substitute for all the matches on each line by adding the /g suffix:

:1s/i/j/g
:p
I decjded I ljke thjs ljne better.

Reading and writing

You can write the current buffer to a file with w, which will also print the total number of bytes written:

:w ed.txt
129

Having done this once, you can omit the filename for the rest of the session:

:w
129

Like most ed commands, w can be prefixed with a range to write only a subset of lines to the file. This would write lines 1 to 4 to the file ed.txt:

:1,4w ed.txt
102

You can use W to append to a file, rather than replace it. This would write lines 3 to 5 to the end of the file ed.txt:

:3,5W
71

You can read in the contents of another file after the current line (or any other line) with r. Again, this will print the number of bytes read.

:r /etc/hosts
205

The output of a command can be included by prefixing it with !:

:r !ps -e
5571

If you just want to load the contents of a file or the output of a command into the buffer, replacing what’s already there, use e, or E if you’ve got an unmodified buffer and don’t care about replacing it:

:e ed.txt
173

If you don’t like seeing the byte counts each time, you can start ed with the -s option for “quiet mode”.

Familiar syntax

Almost all of the above command sets will actually be familiar to vi users who know a little about ex, or Vimscript in Vim. It will also be familiar to those who have used sed at least occasionally.

Once you get good with ed, it’s possible you’ll find yourself using it now and then to make quick edits to files, even on systems where your favourite screen-based editor will load promptly. You could even try using ex.

34 thoughts on “Actually using ed

  1. You forgot to mention a very important feature: You can use regular expressions in ranges too, and thus for example use .,/^$/d to kill the rest of the paragraph (or .,/^$/c to change the rest of the paragraph).

  2. Small typo, when introducing g/re/m$, you probably meant “To move every line containing the expression /re/ to the bottom of the file” instead of to the top. Thanks for the great article.

  3. also: use HERE docs to do little ed scripts: ed $filename <<EDIT a asdfasdf . w q EDIT

    also: ed changes a file ‘in place’. so do not rely on unportable sed -i to do this.

  4. In 1980 my first real programming project was to modify ed into em or “editor for mortals”. I had to find every occurrence of ‘puts(“?”)‘ and replace it with an appropriate error message. I ended up knowing more about ed than anyone except the guy who wrote it. It was a great way to learn UNIX systems programming. Ironically, I did much of my text editing in ded or “display editor” which was one of the first full screen editors for UNIX. It had the drawback that I had to reboot the PDP 11/23 into single user mode to get enough cycles to make the editor usable. Everyone else was happily using em even if they had to occasionally ask me what my somewhat inscrutable error messages meant.

  5. I really love ed. The fact that vi started as a feedback screen for ed says a lot. Plus the regular (sic) linguistic shared between ed/sed/vi is really nice.

    To play the minimalist extremist I’d say everybody should be introduced to computers using ed. It’s nakedness removes the magic.

    Thanks !

  6. In my first C programming course, we used AT&T 3B2s with a terminal (I think the 5620). For the first half of the course the instructor forced us to use ‘ed’ so that we would concentrate on each line. By the time we switched to ‘vi’ for the second half of the course, a complete screen for a function seemed incredibly luxurious. Good times.

  7. I’m not sure if it’s a distribution specific feature, but I think to print out the whole buffer with line numbers you need to use %pn instead of %n. Thanks for this tutorial, it’s really good.

    • Hmm … I think it might be. GNU ed doesn’t seem to require pn, just n suffices. Do you know which version of ed requires pn? If it’s a common one then I’ll update the article for compatibility’s sake.

      • V7 ed was rather primitive in this regard, no n or pn. It looks like this behaviour wasn’t standard in BSD-land until 4.4BSD when H was also added, but does go all the way back to System III so it’s definitely an AT&T addition possibly from a later research unix. But nowhere have I found code that required pn, it’s either no n or n on its own. Hope that helps.

  8. I wish I could remember where I saw it but as another indication of the hardware constraints at the time, I read somewhere the reason for a lack of a prompt in ed (by default, at least)

    Apparently when a prompt was added it made the editor feel slower because users now waited for the prompt befor typing their next command, rather than just going ahead and typing the next one while the machine worked on the previous command. Without a prompt it wasn’t obvious that the previous command was still running (unless it was an output command).

  9. Hi,

    Am relatively new to the world of Unices so the introduction to ed was a welcome. I am however understanding what you mean by screen-oriented and teletype. Would it be possible to elaborate (in very layman terms) what you mean?

    • ed is a line editor, meaning it works by accepting lines of input and printing lines of output; it doesn’t dynamically update areas of the terminal like vi does to present a “screen” as a screen or visual editor such as vi or emacs does, the kind of editor in which you can move a cursor around the buffer.

      Teletype devices are the precursor to modern terminal emulators; they were electromechanical typewriters that physically printed letters onto paper. Therefore editors like ed made a lot more sense for these kinds of terminals as even a little familiarity with a terse editor could save you a lot of ink and printing time.

      • Thanks Tom. Sorry for being such a n00b bu want to clarify what you meant by “meaning it works by accepting lines of input and printing lines of output; it doesn’t dynamically update areas of the terminal like vi does to present a “screen” as a screen or visual editor such as vi or emacs does, the kind of editor in which you can move a cursor around the buffer”. I understand that ed is a line editor that effectively takes your input and based on that input provides an output. Hopefully I am right so far. What I don’t follow is what you mean by dynamically update areas of the screen? For example in my basic understanding of vi, I enter text in INSERT mode and then am able to view the content in COMMAND mode. So do you mean there is no definition in vi akin to ed that refers to an input that returns an output?

        • The pivotal difference is that ed accepts lines of input, and prints lines of output. The terminal emulator simply scrolls just as if you’d used a tool like ls or grep. Lines on the terminal are never changed, only added.

          vi and other screen editors are different in that they set up a “screen” over the terminal and update different areas of it dynamically; toolbars, scrolling through buffers, windows, and splits. That kind of editor simply would not have worked on a teletype.

          • Thanks Tom. I would imagine that ed would not have the ability to scroll back as you do with vi. It would simply output a result, fill the screen and that would be it. Does that mean you would be able to combine ed with some sort of pager or does it have its own?

          • Yes, you’re on the money now. A pager shouldn’t really be necessary with most modern terminal emulators or multiplexers; an xterm should allow you to scroll back over output, and you can do the same in screen or tmux (my favourite).

  10. Pingback: 2012w25 « blog.padowi.se

  11. ed and ex are quite similar, and ed was developed around 1970 , ex then with vi , around 1976 if I am correct. So ed is the oldest editor I think and useful when we need it in the command line and script mode, and so the ex., whien vi can not be used.

    • Yep. That’s also the reason we have the separate EDITOR and VISUAL variables too, but the difference is pretty academic nowadays with modern terminal emulators.

      You could go your whole career without ever having to actually use ed(1), but it’s interesting to learn all the same, to see where a lot of the ideas in newer visual editors came from.

  12. For example how to add one line in the beginning of the file , well ed can do ti easily from command line: for example: $ printf “1\ni\n#My_Scripts First Line Updated: Date:mm/dd/yy.\n.\n\nw\nq\n”| ed -s file

  13. -tried ed -went nearly berserk(^Z | killall ed) -read this -Perfect!….I like it!….good page! Thanks alot! Bookmarked!

  14. I came across a challenge to add line numbers in a range of a file using the command line. I was able to solve it using sed:

    for i in {1..4}; do sed -i “$i s/^/$i /” file.txt; done

    However, is it possible to use ! on a range in ed? If I could do something like this, it’d be shorter and easier to modify.

    :1,4!awk ‘{print NR,$0}’

    And thanks for this great article! Even though I don’t really ‘use’ ed per se, it’s still interesting to play around with.

Leave a Reply

Your email address will not be published. Required fields are marked *

You can use Markdown if you want.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>