ed(1) is The Right Tool (IV) ================================== Let's have a look at our current TODO list: $ ed TODO.txt 121 ,p TODO LIST + phlog about ed(1) - put rubbish bins outside - buy some tea - buy sugar - make a tea * check postgrey hiccup I need to add a couple more items to the list. Let's do it: a - find the sugar pot - configure Unix V7 outpost - read Shismatrix . First, there is a typo: s/Shi/Schi/ . - read Schismatrix Then, as I said in the last phlog, I really would like to get rid of sugar. Really. I need to "delete" all the lines containing "sugar" (and I have just inserted another one!). If "i" is for "insert", "a" is for "append", "c" is for change, "p" is for "print", "s" is for "substitute", what do you expect to be the command to "delete"? Yes, indeed, the ed(1) command for "delete" is "d", and it requires either an address or a pattern. Let's give it a try: /sugar/d ,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 7 - find the sugar pot 8 - configure Unix V7 outpost 9 - read Schismatrix Wow. The line saying "- buy sugar" has disappeared. Let's undo, and try something else: u 5d ,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 7 - find the sugar pot 8 - configure Unix V7 outpost 9 - read Schismatrix Same outcome! So you can delete a line by using the command "d" and specifying either a line number or the pattern that the line should match. Cool, but sometimes impractical, especially if you have lots of "sugar" you want to get rid of. There is obviously an efficient ed(1)-way of dealing with that. In order to have something non-trivial to work on (i.e., more than one line containing "sugar"), we first "undo" the last change, so that all the "sugar" reappears: u ,n 1 TODO LIST 2 + phlog about ed(1) 3 - put rubbish bins outside 4 - buy some tea 5 - buy sugar 6 - make a tea 7 * check postgrey hiccup 8 - find the sugar pot 9 - configure Unix V7 outpost 10 - read Schismatrix What if we want to print all and only the lines containing "sugar"? We know that the simple search command "/sugar/" won't work, since it prints only the next matching line and then stops. Let's try someting more powerful: g/sugar/p - buy sugar - find the sugar pot We have just used the "global" ed(1) command, indicated by "g". This command looks for matches of the pattern immediately following it (in this case, the pattern is "sugar") and then executes the command indicated right after the pattern itself (in this case, the command is "p" for "print"). So what we have just done is a specific instance of "g/RE/p", which looks for all the lines matching the regular expression "RE" and prints them. Yes, this is exactly where the Unix command grep(1) comes from! [1] The nice thing about the global command is that it can be followed by literally any other ed(1) command. So if we need to delete all the lines containing "sugar", we just give: g/sugar/d ,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 7 - configure Unix V7 outpost 8 - read Schismatrix Now we are talking. This could have become the "gred" command, but the sage dwarves at Murray Hill noticed that it would have been better to have a generic tool to handle generic stream-oriented editing, and the Unix command sed(1) was born instead [2]. Before we save the todo-list, I need to note that I have started working on two items, namely those on line 7 and line 8 (yes, I am indeed putting up an outpost with a working Unix V7 machine for general use, and I need a good name for it, possibly from the Schismatrix universe :P): g/ix/s/-/+/ ,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 7 + configure Unix V7 outpost 8 + read Schismatrix Can you see why the global command did exactly what we expected it to do? Let's dissect it. We asked ed(1) to look globally for the pattern "ix" (g/ix/), and to substitute a "-" with a "+" in the matching lines (s/-/+/). Easy, right? Let's save our work now. w 156 q -+-+-+- [1] http://www.catb.org/~esr/jargon/html/G/grep.html [2] http://www.cs.dartmouth.edu/~doug/reader.pdf