Mac OS Xではsedで\n
が動作しないことがわかりました。具体的には、1つのスペースで区切られた単語を行に分割したいとします
# input
foo bar
I use,
echo "foo bar" | sed 's/ /\n/'
しかし、結果は馬鹿げていて、\n
がエスケープされていません!
foonbar
googleに相談したところ、回避策を見つけました
echo 'foo bar' | sed -e 's/ /\'$'\n/g'
記事を読んでみても、\'$'\n/g'
の意味がいまだにわかりません。誰か説明してくれないか、他の方法があれば教えてください。ありがとうございます
51 Ivan Z. G. Xiao 2011-07-06
brew install gnu-sed
とsed
の呼び出しをgsed
に置き換えることができます
gsed
ではなくsed
として使用するには、インストール後に以下のような指示が表示されます
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"
つまり、次の行を ~/.bashrc または ~/.zshrc に追加します
export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"
33 Ahmed Fasih 2013-04-14
これらも効果があるでしょう
echo 'foo bar' | sed 's/ /\
/g'
echo 'foo bar' | sed $'s/ /\\\n/g'
lf=$'\n'; echo 'foo bar' | sed "s/ /\\$lf/g"
OS X の sed は置換パターンの \n
を解釈しませんが、改行文字の前にリテラルの改行文字を使うことができます。シェルは sed コマンドを実行する前に $'\n'
をリテラル改行で置き換えます
26 Lri 2011-07-07
見つけた回避策では、sed -e
に1つの引数文字列を渡しています
この引数は、おなじみの sed s/ / /g
形式の文字列で終わります
その文字列は、次から次へと2つの部分に分けて作成されます
最初の部分は'...'
形式で引用します
2番目の部分は$'...'
形式で引用しています
's/ /\'
の部分はシングルクォートが取り除かれますが、それ以外の部分はコマンドラインでの見た目と同じように sed に渡されます。つまり、バックスラッシュは bash に食べられず、sed に渡されます
$'\n/g'
の部分はドル記号とシングルクォートが取り除かれ、\n
は改行文字に変換されます
全てを合わせると、議論は次のようになります
s/ /\newline/g
(誠人の声) 「面白かったよ、解くのに時間がかかった
12 Spiff 2011-07-07
式 $'...'
は bash
-ism であり、標準のエスケープシーケンスを展開した ...
を生成します。\'
の前の \'
はバックスラッシュと引用符で囲まれた部分の終わりを意味し、結果として得られる文字列は s/ /\
となります。(そう、文字列の途中で引用符を切り替えることができます。)
POSIX標準のsed
は検索パターンの一部として\n
だけを受け入れます。OS X は FreeBSD sed
を使っていますが、これは POSIX に厳密に準拠しています。GNU はいつものように余分なものを追加して、Linux ユーザはそれをある種の「標準」だと思っています (たぶん、どちらかが標準化プロセスを持っていたら、私はもっと感銘を受けたでしょう)
7 geekosaur 2011-07-06
視覚的に見やすい工夫がある文字列をエコーするだけでOK!
echo 's/$/\'$'\n/g'
results in
s/$/\
/g
これはs/$/\
newline/g
に相当します
もし、newline
の前に余分な\
がなければ、シェルはnewline
をコマンドの終了として解釈してしまいます
1 wisbucky 2017-10-27
この回避策はMacで動作し、Linuxでも実行できます
NL="\n"
if [[ $uname -eq "Darwin" ]]; then
NL=$'\\\n'
fi
echo 'foo bar' | sed -e "s| |${NL}|"
1 Massimo Zerbini 2020-03-10
@max-zerbiniの答えのもう少しコンパクトな形
[[ $(uname) -eq "Darwin" ]] && NL=$'\\\n' || NL="\n"
echo 'foo bar' | sed -e "s| |${NL}|"
0 slm 2020-06-03