ed(1) is The Right Tool ============================= You know: ed(1) is the standard text editor [1], and indeed you would find ed(1) installed in any unix-like system. But I guess most Unix users have never actually tried to open it. The reason is probably that ed(1) is considered "hard-to-use" and "user-unfriendly". I really see no reason for such misconceptions and no motivation for their popularity, so I will try to convince you with concrete examples and scientific evidence that ed(1) is, almost always, The Right Tool to use. This tutorial was originally publised as a series of "phlog" posts (a blog over gopher), in the hope to serve as an "ed-primer" for beginners. The eight posts are reported below in the same order as they appeared in the phlog. The only addition is the last paragraph of Section VII, which was not present in the initial series of phlogs. The references of each section are reported at the end of the section. If you are in a rush and would like to go a proper book, look for the great "Ed Mastery" by Michael Lucas [2]. KatolaZ ed(1) is The Right Tool (I) ================================= First things first: ed(1) is a line editor, meaning that it acts on a line at a time. This is something we are not used to anymore, since our glass-terminals have allowed full-screen character-based editors to evolve. But on teletypes line-based editing makes a lot of sense. This means that we need to tell ed(1) which line we would like to edit and what to do on that line. In this tutorial we will use ed(1) to compile and manage simple TODO lists. This will allow us to explore almost all the ed(1) commands. Let's start by creating the file "TODO.txt" with ed(1): $ ed TODO.txt TODO.txt: No such file or directory Well, welcome to ed(1), aka "The Faithful Silent Servant of Unix masters of old". ed(1) is telling you that it could not find any file named TODO.txt in the current directory (and this is something we knew already) and then it prints a new-line to signal that it is ready to receive commands. Yes, ed(1) has no default "prompt". Actually, it does, and the prompt is just "no-character-at-all". Now let's add a few items to our TODO.txt list: 0a TODO LIST - phlog about ed(1) - put rubbish bins outside - make a tea - check postgrey hiccup . The first line "0a" tells ed(1) that I want to "a-ppend" lines after the 0-th line. Now, the 0-th line actually indicates the start of the buffer. ed(1) accepts my command and waits for the lines to be inserted there. It will keep receiving lines until you input a line containing only a "." (dot) [3]. Then ed(1) will "print" its prompt (which is indeed "no-character") and wait for the next command. Let's ask ed(1) to "p-rint" the whole TODO list then: ,p TODO LIST - phlog about ed(1) - put rubbish bins outside - make a tea - check postgrey hiccup (notice the ed(1) prompt on the last line!). The command "p" is used to print lines. It is normally put after the specification of a range of lines, but in this case "," indicates the range "from the first line down to the last one". Now what happens if you type "p" alone? p - check postgrey hiccup (notice again the ed(1) prompt above). Well, any command in ed(1) modifies the "current line address", which is the line any ed(1) command would implicitely work on if you don't provide any line to it. The command "p" moves the current address to the last line it printed. So ",p" moved the current address to the last line of the buffer, and the following "p" just printed the current line (the last one). Another useful command is "n", which prints lines adding line numbers: ,n 1 TODO LIST 2 - phlog about ed(1) 3 - put rubbish bins outside 4 - make a tea 5 - check postgrey hiccup Oh now that's convenient! Guess how you ask ed(1) to print the third line in your buffer: 3p - put rubbish bins outside While you can use the special marker "$" to indicate "the last line in the buffer": $n 5 - check postgrey hiccup Notice that there is no space between line ranges and the command that applies to them. I guess you have had enough ed(1)-ting for today, and I am not in the right mood to start working at this todo-list now. So let's just save our work for later: w 94 ed(1) prints the number of bytes written in the file TODO.txt. We can now exit ed(1): q $ and you are back to the shell prompt. Now you can see your brand-new TODO list with: $ cat TODO.txt TODO LIST - phlog about ed(1) - put rubbish bins outside - make a tea - check postgrey hiccup $ or by using: $ printf ",p\n" | ed TODO.txt 94 TODO LIST - phlog about ed(1) - put rubbish bins outside - make a tea - check postgrey hiccup $ ;-) -+-+-+- ed(1) was included in Unix-V1 (1971) and was used to write the Unix kernel and all the programs distributed with Unix at least until Unix-V7 (1979) -+-+-+- [1] https://www.gnu.org/fun/jokes/ed-msg.html [2] https://www.tiltedwindmillpress.com/product/ed/ [3] I use a dash "-" to indicate tasks that are still outstanding, a plus "+" for tasks I have started working on, and a star "*" for completed tasks. ed(1) is The Right Tool (II) ================================== Let's get on with our mission of learning the basics of ed(1), the standard text editor of the unix environment. In the first installment of this mini-series of phlogs [1] we saw a few basic commands to add some lines ("a"), print lines ("p", "n") and save a file after you are done ("w"). Using those commands we were able to compile a simple TODO list: $ ed TODO.txt 94 ,n 1 TODO LIST 2 - phlog about ed(1) 3 - put rubbish bins outside 4 - make a tea 5 - check postgrey hiccup Now imagine that we want to insert a new item (buy some tea) just before the line that reminds us to make a tea. We will use the command "i" (for "insert"): 4i - buy some pea . ,n 1 TODO LIST 2 - phlog about ed(1) 3 - put rubbish bins outside 4 - buy some pea 5 - make a tea 6 - check postgrey hiccup Easy, right? While "a" is used to "append" lines after a given line, "i" is instead used to "insert" lines before a given line. As with "a", the "i" commands is ended by a line containing only a ".". If you have ever used vi(1) or one of its many clones, you know that "i" is also the command needed to move to "insert" mode in vi(1). Well, this is not a coincidence. Notice that when we inserted the new line we also introduced a typo: we definitely want to buy some "tea" (not a "pea"). We can "change" the content of a line using the command "c": 4c - buy some tea . ,n 1 TODO LIST 2 - phlog about ed(1) 3 - put rubbish bins outside 4 - buy some tea 5 - make a tea 6 - check postgrey hiccup Notice that ed(1) acts consistently: the three commands used to append, insert, or change lines are all terminated by a line containing a single ".". Actually, the command "c" in general allows us to replace one line (or a range of lines) with another line (or with a range of lines). So if you prefer coffee instead of tea you might use: 4,5c - buy some coffee - buy sugar - make a coffee . ,n 1 TODO LIST 2 - phlog about ed(1) 3 - put rubbish bins outside 4 - buy some coffee 5 - buy sugar 6 - make a coffee 7 - check postgrey hiccup Well, it's evident that having to retype a line just to correct a typo is not exactly what one would call an efficient editing session. Indeed, the command "c" is mostly used when the content of a range of lines has to be substantially modified. Luckily, ed(1) has another command ("s", for "substitute") which allows to easily correct typos, as well as to perform complicate substitutions across any range of lines. That will be the focus of the next phlog in this series :) -+-+-+- [1] gopher://republic.circumlunar.space/0/~katolaz/phlog/20190505_ed_lists.txt ed(1) is The Right Tool (III) =================================== A todo-list is useful if you start working on the items and you are eventually able to remove some items from the list, sooner or later... Our current list looks like this: ,n 1 TODO LIST 2 - phlog about ed(1) 3 - put rubbish bins outside 4 - buy some coffee 5 - buy sugar 6 - make a coffee 7 - check postgrey hiccup Now it is clear I have started phlogging about ed(1), so the first item should somehow be updated. I have come up with a simple convention to identify the status of items in a todo-list, by using the first character of the item: a "-" indicates an outstanding item, a "+" indicates something I have started working on, and a "*" marks a completed item. In this case, I would like to change the "-" in the first item with a "+". Let's start searching for the line where I mentioned "phlog": /phlog/ - phlog about ed(1) Yep. An expression like "/expr/" is used in ed(1) to search for a pattern. ed(1) search for the first occurrence of that pattern and prints it. Most versions of ed(1) will also wrap the search around and start from the beginning if they reach the end of the file. Now the current line (the so-called ".") is the one containing the item I want to edit. Let's "substitute" the first "-" in the line with a "+": s/-/+/ p + phlog about ed(1) Yeah, it's that simple. The command "s" (for "substitute") replaces the first occurrence of the pattern between the first pair of "/" following it with the pattern between the second pair of "/" following it. In this case, we simply replaced "-" with "+". The command "s" accepts a line (or a range of lines) to work on. So if we are indeed working on all the items in the todo-list, you can change their state with a single command: 1,$s/-/+/ ,p TODO LIST + phlog about ed(1) + put rubbish bins outside + buy some coffee + buy sugar + make a coffee + check postgrey hiccup Well, that's powerful, right? But in my case, well, it's not true, since I have not put the rubbish outside and some other items are in a different state. Let's revert out last change: u ,p TODO LIST + phlog about ed(1) - put rubbish bins outside - buy some coffee - buy sugar - make a coffee - check postgrey hiccup The command "u" (for "undo") will do that (i.e., revert the effect of the last command). Try to see what happens if you keep pressing "u" :) The cool thing about "s" is that the line it has to act upon can be expressed with a pattern. For instance, I have solved the hiccup in postgrey, so I can change the status of the line containing "postgrey" to "done" by replacing the leading "-" with a "*". But I don't remember which line is it, so I can use the command: /postgrey/s/-/*/ ,p TODO LIST + phlog about ed(1) - put rubbish bins outside - buy some coffee - buy sugar - make a coffee * check postgrey hiccup Remember that the "." is set at the last edited line: