20

I recently found out that this simple sed expression work fine on Linux or under Cygwin but fails on Mac with an "undefined label" error:

$ sed '/SUCCESSFUL/d ; /\[java\]/!b label; s/\s\+\[java\]//; /^\s*$$/d; /Compiling/!d; :label /^\s*$$/d; s/^/monitor: /'
sed: 1: "/SUCCESSFUL/d ; /\[java ...": undefined label 'label; s/\s\+\[java\]//; /^\s*$$/d; /Compiling/!d; :label /^\s*$$/d; s/^/monitor: /'

sed on MacOS is a BSD variant with different options than the GNU counterpart. However man sed clearly indicates the MacOS version of sed supports labels, so why this error, and most important how to solve it?

ralfoide
  • 2,705
  • 4
  • 23
  • 21

3 Answers3

23

A quick fix for others with similar problems (on MacOS) might be:

prepend your string expression with an empty string: ''

For example: instead of

sed -i 's/foo/bar/g' text.txt

write:

sed -i '' 's/foo/bar/g' text.txt

dopexxx
  • 2,298
  • 19
  • 30
  • 2
    perfect, but why does this work? Does it work the same on Linux as macos? – Samuel Neff Apr 13 '22 at 18:03
  • I think the first and the second command do the same if executed on Linux and mac respectively. The first doesn’t work on Mac but I’m unsure what happens with the second on Linux. – dopexxx Apr 13 '22 at 18:09
  • there's a whole lot of blah blah blah on this page - thank goodness you cleared up that the label needed be provided as an empty string in mac. – WEBjuju May 31 '22 at 21:34
  • An alternative could be to use `-i=''` this worked for me (as part of a Snakemake rule) when `-i ''` didn't. – Cornelius Roemer Oct 04 '22 at 17:06
  • See https://stackoverflow.com/a/7573438/61109 – Niels Bom Nov 16 '22 at 10:51
10

The name of the label terminates with the first literal newline, not at the semi-colon. There are two easy ways to solve the problem. Add literal newlines:

 sed '/SUCCESSFUL/d 
    /\[java\]/!b label
    s/\s\+\[java\]//
    /^\s*$$/d; /Compiling/!d
    :label
    /^\s*$$/d
    s/^/monitor: /'

Or use multiple -e options:

sed -e '/SUCCESSFUL/d ; /\[java\]/!b label' \
  -e 's/\s\+\[java\]//; /^\s*$$/d; /Compiling/!d' \
  -e':label' -e'/^\s*$$/d; s/^/monitor: /'
William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • This is true in label defining commands as well as branching commands which reference labels. I don't see anything in POSIX which says that these commands don't have to observe the semicolon convention. Why don't Apple fix these things? It's still present in the latest Mac OS on M1. – Kaz Mar 24 '21 at 17:04
6

There are a bunch of similar questions on SO but most of them are due to the behavior of -i differing between platforms, so this is different.

In this case, the issue is rather simple: it seems like label references can only go backwards in the BSD version of sed, whereas the GNU version allows to use forward references. That is on MacOS, the :label must appear before the b label.

The solution is to rewrite the expression to either define the label before the branch, or in the case of the expression above realize the branch is a kind of "if this pattern is not present ... jump ahead". In this case the expression can be expanded to not need the label in the first place:

sed '/SUCCESSFUL/d ; /\s+\[java\]\s*/d; /\[java\]/s/\s\+\[java\]//; /Compiling/!d; /^\s*$$/d; s/^/monitor: /'
ralfoide
  • 2,705
  • 4
  • 23
  • 21
  • 1
    +1 and Good catch. It also helps to visualize the issue as indented code, instead of with one-liner-itis :-) Good luck. – shellter Sep 04 '12 at 22:17