Linux

Mac sedコマンドの-iオプションでunterminated substitute patternのエラー

MacでLinuxサーバ用のシェルスクリプトをいじっているときにsedコマンドのBSD版/GNU版の違いに遭遇したのでメモっておきます。

スポンサーリンク

事象

sedコマンドの -i(in-place=ファイルを直接書き換える)オプションを実行したところ以下のようなエラーとなりました。

$ sed -i 's/foo/bar/' src.txt
sed: 1: "src.txt": unterminated substitute pattern
$

原因と対応

MacのBSD版のsedコマンドでは -i オプションのあとに、置換時に自動的に作成されるバックアップファイル用のsuffixの指定が必要でした。

そのためバックアップファイルなしに直接ファイルを書き換える場合は、-i ”(空のクオーテーション) と無理矢理にでもsuffixを指定する必要があります。

## -i の後ろに''を記述
$ sed -i '' 's/foo/bar/' src.txt
$
## バックアップファイルは作成されない
$ ls -l src.txt*
-rw-r--r--  1 zoo200  staff  4  2 26 14:29 src.txt
$
## バックアップファイルのsuffixを.bakと指定
$ sed -i '.bak' 's/foo/bar/' src.txt
$
## 自動的にバックアップファイルが作成される
$ ls -l src.txt*
-rw-r--r--  1 zoo200  staff  4  2 26 14:29 src.txt
-rw-r--r--  1 zoo200  staff  4  2 26 14:17 src.txt.bak
$
$ diff src.txt src.txt.bak
1c1
< bar
---
> foo
$

manに例がちゃんと書いてありました。

$ man sed
...
     -i extension
             Edit files in-place similarly to -I, but treat each file independently from other files.  In particular, line numbers in each file start at 1,
             the “$” address matches the last line of the current file, and address ranges are limited to the current file.  (See Sed Addresses.) The net
             result is as though each file were edited by a separate sed instance.
...
     Replace all occurances of ‘foo’ with ‘bar’ in the file test.txt, without creating a backup of the file:

           sed -i '' -e 's/foo/bar/g' test.txt
...
$

Tips

GNU版のsedコマンド

LinuxのGNU版のsedコマンドは-iのあとにsuffixをつけなくても問題なく実行できます。

$ sed -i 's/foo/bar/' src.txt
$
$ man sed
...
       -i[SUFFIX], --in-place[=SUFFIX]

              edit files in place (makes backup if SUFFIX supplied)
...
$

GNU版でクオーテーションを記述する場合は、-iのあとにスペースがあるとエラーとなります。

$ sed -i '' 's/foo/bar/' src.txt
sed: s/foo/bar/ を読み込めません: No such file or directory
$
## manに書いてあるとおりスペースがないのが正しい
$ sed -i'' 's/foo/bar/' src.txt
$

逆にBSD版はスペースがないとエラーになるのでややこしいです。

$ sed -i'' 's/foo/bar/' src.txt
sed: 1: "src.txt": unterminated substitute pattern
$

なおBSD版でもsuffixを明示的に指定する場合は、GNU版と同様にスペースがなくてもOKです。

$ sed -i'.bak' 's/foo/bar/' src.txt
$
## クオーテーションで囲まなくてもOK
$ sed -i.bak 's/foo/bar/' src.txt
$

ファイル名によりエラーメッセージが異なる

記事を書いているときに気づいたのですが、ファイルの先頭文字の違いにより出力されるエラーが異なりました。

$ sed -i 's/foo/bar/' s.txt
sed: 1: "s.txt": unterminated substitute pattern
$
$ sed -i 's/foo/bar/' a.txt
sed: 1: "a.txt": command a expects \ followed by text
$
$ sed -i 's/foo/bar/' b.txt
sed: 1: "b.txt": undefined label '.txt'
$
$ sed -i 's/foo/bar/' d.txt
sed: 1: "d.txt": extra characters at the end of d command
$

おそらくこちらに定義されているsedコマンドの引数と認識されて、エラーメッセージが異なると思われます。

今回は以上です〜ノシ

参考

(`・ω・´)ノ アリガトウゴザイマス!!

What is wrong with my string substitution using sed on Mac OS X?