Using the `ed` editor
Tags: editors, ed, oldschool
Published: 2020-07-29 16:34:49

---
title: Using the `ed` editor
published: true
description: A great example of programmatic file editing
tags: editors, ed, oldschool
//cover_image: https://direct_url_to_image.jpg
---
Hands up who's heard of the `ed` editor.

...

OK, that's a few of you. Now keep your hand up if you've *used* it, for anything more than just to see what it's about.

...

Oh, just me then. You're all missing out. It's a brilliant tool that, in a few limited circumstances, is exactly what you need.

You're all familiar with `sed` I assume? `sed` is the Stream EDitor, and to a first approximation[^1] it takes a small program and a file (or STDIN) as input, applies the program to every line of the file, and spits the results out one line at a time. A typical invocation looks like this:

```shell
sed 's/regexp/substitution/' input > output
```

That will replace any instance of `regexp` in the input data with `substitution`, and write the results to a new file.

Another typical invocation of `sed` looks like this:

```shell
some_command | sed 's/regexp/substitution/' | other_command
```

That is, we've got a pipeline where a program produces some output, we massage that output using a sed script, and pass the results on for further processing elsewhere.

But what if, instead of loading some pre-defined commands and feeding in some data, you want to load a file and feed in some editor commands? That is, instead of programatically producing the data, you want to programatically produce the commands?

Enter `ed`. It is a direct ancestor of `sed`, and `sed` can do everything that `ed` can do, but even so, in just the same way that Javascript can do everything that Python can do and vice-versa, sometimes a tool that specialises differently is what you want. Using `ed` you can do things like this[^2]:

```shell
some_command | ed -s somefile | other_command
```

where `some_command` generates a *program* which `ed` then uses to transform the file. If we want to do the same transformation as we did a moment ago using `ed` instead of of `sed` then we'd do something like this:

```shell
printf "%s\n" %s/regexp/substitution/ %p | ed -s somefile | other_command
```

but of course the commands we feed in to `ed` can be anything you like, composed on the fly at run-time, which I think you'll agree makes it an elegant tool for a more civilized shell script. Let's break that command down.

`ed -s somefile` starts the editor with `somefile` already loaded; the `-s` tells it to be silent and suppress all output except when we explicitly tell it to say something. The `printf` command turns its arguments into two lines of text thus:

```
%s/regexp/substitution/
%p
```

Those are then the STDIN to the `ed` command. And gosh, doesn't that first line look rather like a `:command` you might type into `vim`? The second line's `%p` means "for all lines (the `%`) print to STDOUT (`p`)" - in general any list of commands you pass to `ed` will end with `%p` or `w`, to write the edited file to STDOUT or to a file.

You can of course do anything else that the editors you are used to are capable of. I won't give you a full tutorial on it here ([GNU's online documentation](https://www.gnu.org/software/ed/manual/ed_manual.html) is a good place to start), but if you are a `vi(m)` user it will be somewhat familiar[^3], as the `vi` family of editors are, just like `sed`, descendants of `ed` too.

[^1]: sed is Turing-complete, so as a second approximation it is [a game of Tetris](https://github.com/uuner/sedtris)

[^2]: Yeah yeah, `sed -f /dev/stdin`. Shush. `ed` feels more editory anyway, which (at least for me) is a good enough reason to use it even if `sed` can be forced to turn itself inside-out like that.

[^3]: but only somewhat familiar. `ed` is a line-oriented editor, so for example the `d` command means "delete the current line" which in `vim` would be `dd`. You don't navigate to a particular character and delete or add text, you navigate to a line and delete or add *lines*. To edit a line in-place you use `s/.../.../`.