MAC OSXでsedした時の sed: RE error: illegal byte sequence エラー

なぜかよくわからんが、OSXsedを使っているとたまにタイトルのエラーに遭遇することがちょこちょこあります。

対処方法はロケールをC(POSIX)に指定することらしいけれど、そもそもなぜこれが発生するのかよくわからなかったので調べてみました。

発生した環境

今回このエラーが発生したのは、特定のディレクトリ以下にあるファイル内の文字列を一括置換しようとしたタイミングでした。 コマンドで言うとこんな感じ。

$ find . -type f | xargs sed -i .bak s/hoge/fuga/

エラーとしてはタイトルの通りsed: RE error: illegal byte sequenceとなっていました。

改めて対象ファイルを見てみる

実際のものは見せられないので、それっぽい再現ですがこんな感じでした。

$ ls -la
total 32
drwxr-xr-x   5 t-sei  2125026295   170  1 13 23:21 .
drwx------+ 24 t-sei  2125026295   816  1 13 23:17 ..
-rw-r--r--   1 t-sei  2125026295  6149  1 13 23:19 .DS_Store
-rw-r--r--   1 t-sei  2125026295     5  1 13 23:20 fuga.txt
-rw-r--r--   1 t-sei  2125026295     5  1 13 23:20 hoge.txt

上から三番目に何か怪しい奴が居ますね。彼をsedに投げてみます

$ sed s/hoge/fuga/ .DS_Store
sed: RE error: illegal byte sequence

ビンゴ!さっそく犯人を見つけました。やはりOSXユーザは.DS_Storeとうまく付き合う方法を学ばねばならないようです。

なぜsed.DS_Storeを渡すとエラーになるのか?

エラー文言を見る限り、不正なバイト列が渡ってきてるんだよねという話のようです。 しかし一方で、ロケールの設定を変更することでこのエラーは発生しなくなりますので、なぜなのか調べてみました。

とりあえずググってみると、UNIX WARE7のsedページでそれっぽい記述を見つけました。

sed環境変数 LC_CTYPE (environ(5) の LANG を参照) に指定されたロケールに従って、script ファイル中の注釈の補助コードセットを処理します。 ただし、後述する y コマンドに関しては例外です。ed(1) で説明しているように、正規表現ではパターン検索をバイト単位ではなく文字単位で行います。

なるほど、LC_ALL(LC_CTYPE)の設定を変えることで挙動が変わる理由がわかりました。

つまり、OSXのデフォルトロケール設定のsedでは.DS_Storeのようなバイナリファイルを解釈出来ないということのようです。

ちなみにLC_CTYPE環境変数OpenGroupのオンラインヘルプによると、 文字などのテキストデータのバイトシーケンスの解釈や文字の分類をする際に参照されるようです。

(追記)挙動と環境変数に関してはOpenGroupの sedページ見るのが一番よさそうだった。

私の環境では下記のようになっていました。

$ locale
LANG="ja_JP.UTF-8"
LC_COLLATE="ja_JP.UTF-8"
LC_CTYPE="ja_JP.UTF-8"
LC_MESSAGES="ja_JP.UTF-8"
LC_MONETARY="ja_JP.UTF-8"
LC_NUMERIC="ja_JP.UTF-8"
LC_TIME="ja_JP.UTF-8"
LC_ALL=

ja_JP.UTF-8を期待しているところにバイナリファイルである.DS_Storeを叩きこまれて解釈出来ずに困ってしまったということでした。

今後、同様のエラーが出た時に参考にしていただければ幸いです。