Newlines in sed on Mac OS X

46

18

I find that \n doesn't work in sed under Mac OS X. Specifically, say I want to break the words separated by a single space into lines:

# input
foo bar

I use,

echo "foo bar" | sed 's/ /\n/'

But the result is stupid, the \n is not escaped!

foonbar

After I consulted to google, I found a workaround:

echo 'foo bar' | sed -e 's/ /\'$'\n/g'

After reading the article, I still cannot understand what \'$'\n/g' means. Can some one explain it to me, or if there is any other way to do it? Thanks!

Ivan Z. G. Xiao

Posted 2011-07-06T16:01:32.967

Reputation: 2 115

Possible duplicate: http://stackoverflow.com/questions/21621722/removing-carriage-return-on-mac-os-x-using-sed

– hyiltiz – 2017-04-19T07:05:30.500

1this would probably work too: echo "foo bar" | tr ' ' '\n' – glenn jackman – 2011-07-06T18:17:18.170

3Thanks for the advice. But currently I just use the above case as an example, I do need to know how to escape a \n. – Ivan Z. G. Xiao – 2011-07-06T22:50:01.227

Answers

29

You can brew install gnu-sed and replace calls to sed with gsed.


To use it as sed instead of gsed, brew helpfully prints the following instructions after you install:

GNU "sed" has been installed as "gsed".
If you need to use it as "sed", you can add a "gnubin" directory
to your PATH from your bashrc like:

    PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"

That is, append the following line to your ~/.bashrc or ~/.zshrc:

export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"

Ahmed Fasih

Posted 2011-07-06T16:01:32.967

Reputation: 679

Using the same software on all platform is way easier (at last to me) than dealing with every specificities of the Mac version. Beware, the option is now --with-default-names and not --default-names. However, this option did not worked on my installation, so I had to put a alias gsed=sed in my ~/.profile to make it work. – Clément – 2015-05-11T11:40:11.813

4This is an ugly workaround. It doesn't explain why sed on OS X behaves the way it does and makes the incorrect assumption that gnu-sed is more correct. Don't be a GNU-addict and stick to POSIX standards to avoid problems in the long run. – octosquidopus – 2015-10-08T02:13:24.433

It is how Apple should make its OS works by default. Why use the name of a program and then make it works differently?
I waste an hour because of this...
– rascio – 2017-04-04T10:58:14.413

@rascio - because OS X is BSD-like and Linux is Linux-like. – iAdjunct – 2018-05-30T14:30:20.080

@rascio I think you'll find that GNU sed is the newcomer; BSD sed dates back to 1979 (see https://en.wikipedia.org/wiki/Version_7_Unix).

– Duncan Bayne – 2018-09-10T05:19:00.687

This is the preferred answer in most cases I think. If you get a compile script in your lap you don't want to spend time modifying it just to make it work with OSX. Instead, simply do brew install gnu-sed, alias sed='gsed', sh compile.sh, and remove the alias when you're done (unalias sed). – Robin Keskisarkka – 2019-04-02T12:53:49.753

Correction: For the alias to work correctly inside the script, the command should be source compile.sh – Robin Keskisarkka – 2019-04-02T13:03:06.490

brew install gnu-sed --with-default-names gives me Error: invalid option: --with-default-names – k1eran – 2019-12-18T16:01:03.353

1@k1eran thanks for the note, fixed. – Ahmed Fasih – 2019-12-19T02:15:19.247

24

These would also work:

echo 'foo bar' | sed 's/ /\
/g'

echo 'foo bar' | sed $'s/ /\\\n/g'

lf=$'\n'; echo 'foo bar' | sed "s/ /\\$lf/g"

OS X's sed doesn't interpret \n in the replace pattern, but you can use a literal linefeed preceded by a line continuation character. The shell replaces $'\n' with a literal linefeed before the sed command is run.

Lri

Posted 2011-07-06T16:01:32.967

Reputation: 34 501

4Why does sed $'s/ /\\\n/g' work, but not sed $'s/\\\n/ /g'? – Alec Jacobson – 2016-01-03T23:03:48.327

1@alec You can't use sed even in linux/unix to remove newlines because its parsed/split at each newline. If you run this in linux/unix, it won't do anything either: echo -e 'foo\nbar' | sed 's/\n//' – wisbucky – 2017-10-27T01:05:21.600

11

The workaround you found passes a single argument string to sed -e.

That argument ends up being a string in the familiar sed s/ / /g format.

That string is created in two parts, one after the other.

The first part is quoted in '...' form.

The second part is quoted in $'...' form.

The 's/ /\' part gets the single-quotes stripped off, but otherwise passes through to sed just as it looks on the command-line. That is, the backslash isn't eaten by bash, it's passed to sed.

The $'\n/g' part gets the dollar sign and the single-quotes stripped off, and the \n gets converted to a newline character.

All together, the argument becomes

s/ /\newline/g

[That was fun. Took a while to unwrap that. +1 for an interesting question.]

Spiff

Posted 2011-07-06T16:01:32.967

Reputation: 84 656

7

The expression $'...' is a bash-ism which produces ... with the standard escape sequences expanded. Th \' before it just means a backslash followed by the end of the quoted section, the resulting string is s/ /\. (Yes, you can switch quoting in the middle of a string; it doesn't end the string.)

POSIX standard sed only accepts \n as part of a search pattern. OS X uses the FreeBSD sed, which is strictly POSIX compliant; GNU, as usual, adds extra stuff and then Linux users all think that is some kind of "standard" (maybe I'd be more impressed if either of them had a standards process).

geekosaur

Posted 2011-07-06T16:01:32.967

Reputation: 10 195

Now I understand the $'...' part. But... what is s/ /\? What do you mean by switch quoting? – Ivan Z. G. Xiao – 2011-07-06T22:53:31.003

2'...' is one kind of shell quoting; $'...' is another. There's also "..." and \x to quote a single character. You can combine those in a single word, which is what was being done there, switching from a normal '' string to a $'' string to translate the \n. As for the rest, it's building up a sed command (s/text/replacement/flags). In this case the command is started, including a backslash at the end to protect the literal newline that the $'\n/g' appends. The result is to replace all (the /g flag) spaces with newlines. – geekosaur – 2011-07-06T23:01:08.750

1

There's a very easy to visually see what's happening. Simply echo the string!

echo 's/$/\'$'\n/g'

results in

s/$/\
/g

which is equivalent to s/$/\newline/g

If you didn't have the extra \ before the newline, the shell would interpret the newline as the end of the command prematurely.

wisbucky

Posted 2011-07-06T16:01:32.967

Reputation: 1 522