1

Working on a question for codegolf.se, trying to do a simple text replacement.

  • Given a file coin.txt containing 1 word, either heads or tails
  • I have an ed script flip.ed as follows:
s/heads/1/n\
s/tails/-1/n\
wq
.
  • Then I execute it from the terminal (macos, zsh), as follows: (per "Ed Mastery", chapter 111)
[~/golf/ed]λ ed coin.txt < flip.ed; cat coin.txt  
6
?
tails

Expected the script to change tails to -1. I get the same problem from BSD and GNU ed (/bin/ed and /usr/local/bin/ged, and TIO.run for that matter). Neither one seems able to execute multiple replacements.

Here's an example on TIO.run, I think it's running GNU ed.

Is there a problem in my ed script? Or is it a bug in the mighty ed??

Update

A solution, courtesy of @ed1conf on twitter. Uses g prefix to only do the replacement on matching lines, thus avoiding errors that broke my original script. Try it online!

g/heads/s//1/
g/tails/s//-1/
wq
.
  • 1
    What is your intention with the /n suffix in the s/// command? I'm not familiar with it. – Jeff Schaller Aug 14 at 16:36
  • n is like p for print, but n gives line numbers as well. Seems to have no effect in this situation, I removed it in the TIO example – roblogic Aug 14 at 16:39
  • 1
    Why do you have those backslashes there? tio.run/##S035/1@nWD8jNTGlWN9QnwvILknMzCnW1wVyygu59P7/B8txgUX//… – muru Aug 14 at 17:03
  • I was following an example from the "Ed Mastery" book, thanks for that solution! I couldn't make it work :P – roblogic Aug 14 at 17:18
  • The one example I saw from muru had two lines in the input, which made both searches successful. I understood your input to be one line of one word. – Jeff Schaller Aug 14 at 17:20
2

You can do this, but you cannot send the commands via redirection or a regular file.

From the GNU ed online manual:

When an error occurs, if ed's input is from a regular file or here document, then it exits, otherwise it prints a '?' and returns to command mode.

One of the two commands will fail, since the word in the file cannot match both patterns, and so ed exits -- whether on line 1 because it doesn't match, or on line 2 because it doesn't match (and so exiting before the changed file is saved).

The commands would work (minus the backslashes and suffixes) via stdin:

printf '%s\n' 's/heads/1/' 's/tails/-1/' 'wq' | ed -s coin.txt

You'll get a ? to stderr because the s/heads/1/ failed (which you could redirect away), but the replacements all happen as expected.

Note that I dropped the trailing . command, as the q command is enough to exit ed.


A workaround, posted by ed(1) Conference on Twitter, is:

g/heads/s//1/
g/tails/s//-1/
wq

or, without abbreviations in the s/// portion:

g/heads/s/heads/1/
g/tails/s/tails/-1/
wq

These variations work because the g commands do not fail, even if no substitutions are performed.

  • 3
    BSD ed stops processing at the non-match, and won't get to the wq. – Kusalananda Aug 14 at 17:15
  • 1
    Very interesting! Thanks for the testing. I tried some variations, like g /heads/ s/heads/1/ but even that "fails" enough to bail out. – Jeff Schaller Aug 14 at 17:16
  • All true but @ed1conf on twitter demonstrated a version that works using the g prefix to avoid the errors. – roblogic Aug 14 at 17:35
  • 1
    I could have sworn I tried that (the expanded g/heads/ s/heads/1/ version) but failed. If you have a link to the tweet, I'd love to include it. – Jeff Schaller Aug 14 at 17:52
  • 1
    Interestingly, the script works with g/heads/... but not with g /heads/... despite the info page showing a space between g and /RE/. The online web page does not have a space after g. – Jeff Schaller Aug 14 at 18:22
1

With sed, you could do it with

sed 's/heads/tails/;t;s/tails/heads/'

In ed, the t command is a completely different command and won't "branch if the most recent substitution did something".

With GNU ed, you could do

printf '%s\n' '1s/heads/T/' '1s/tails/H/' '1s/H/heads/' '1s/T/tails/' 'wq' | ed -s coin.txt

to do your flip, but it may not work in BSD ed as the processing of the commands stop at the first substitution that does not match, because

When an error in the input script is encountered, or when an error is detected that is a consequence of the data (not) present in the file or due to an external condition such as a read or write error:

  • If the standard input is a terminal device file, all input shall be flushed, and a new command read.

  • If the standard input is a regular file, ed shall terminate with a non-zero exit status.

(that's from the POSIX standard)

The errors you get from GNU ed when running this script are "No match" errors (visible if you insert the H command first in the script), and can safely be ignored.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.