LoginSignup
29
26

More than 5 years have passed since last update.

sedの-iオプションの非互換

Posted at

sed コマンドには -i というファイルを書き換える時に便利なオプションがある。

$ cat <<EOF > test.txt
foo
bar
baz
EOF
$ sed -i'.bak' 's/a/A/g' test.txt
$ ls
test.txt
test.txt.bak
# 書き換えられたファイル
$ cat test.txt
foo
bAr
bAz
# 元のファイル
$ cat test.txt.bak
foo
bar
baz

さて、バックアップが欲しい時には拡張子に空文字列を渡したいのだがここでGNUとBSDの互換性の問題が発生する。

Ubuntuだとこういう挙動をする

$ sed -i'' 's/a/A/g' test.txt
$ cat
foo
bAr
bAz

普通のようだ。次にOS Xで実行してみる。

$ sed -i'' 's/a/A/g' test.txt
sed: 1: "test.txt": undefined label 'est.txt'

これは -i の直後に続いた'' がなかったことにされて -i として扱われ、's/a/A/g' が拡張子、 test.txt がsedコマンドとして解釈されている。

これを避けるには -i'' の間にスペースを開けてあげれば良い。

$ sed -i '' 's/a/A/g' test.txt

ところがUbuntuでスペース付きを実行するとこうなる。

$ sed -i '' 's/a/A/g' test.txt
sed: s/a/A/g を読み込めません: そのようなファイルやディレクトリはありません

Ubuntuのsedは -i の直後に文字列が続かなかったらそのまま空文字列が渡されたとして解釈し、次の '' をsedコマンド、 's/a/A/g' 以後をファイル名として解釈しようとする。

まとめると、こうだ。

-i'' -i ''
Ubuntu 正常 '' がsedコマンドに
OS X '' が無視される 正常

空文字列を渡す互換性のある方法がない。

因みに空でない文字列を渡すとこうなる。

-i'str' -i 'str'
Ubuntu 正常 'str' がsedコマンドに
OS X 正常 正常

Ubuntuの方は一貫性があるが、OS Xは何をとち狂った、といった感じだ。

まあ、これは -i'' は見た目上 -i'' に空文字列を渡しているが実際はシェルレベルで -i と解釈されるので空文字列を渡したのか次に文字列が続くのかコマンドに渡った時には判断出来ないからだ。
BSDツールを作った人が空文字列を特別扱いした訳ではなくて逆に渡される文字列が空の時のエッジケースを考えなかったらこうなったのかもしれない。
GNUツールの方はそこも考慮して作ったのかもしれない。

シェルの非互換問題は根深い。

29
26
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
26