macos – Mac OS X での sed の改行

bash mac macos regex sed

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-sedsedの呼び出しを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


タイトルとURLをコピーしました