ディレクトリを再帰的に下りてファイル内の文字列を一括置換するために以下のコマンドがあります。
例:日本をJapanに置換する場合
find . -type f | xargs sed -i "s/日本/Japan/g"
これは正常に日本→Japanに置換できるのですが
ユーザをUserに置換する場合次のエラー文が表示されます。
sed: -e expression #1, char 17: unterminated `s' command
コマンドは以下です。
find . -type f | xargs sed -i "s/ユーザ/User/g"
ウォッチをWatchに置換する場合
find . -type f | xargs sed -i "s/ウォッチ/Watch/g"
以下のエラーが表示されます。
sh: -c: line 0: unexpected EOF while looking for matching ``'
sh: -c: line 1: syntax error: unexpected end of file
マルチバイトの文字によってエラーになるようです。
ユーザの場合は『ー』が原因のようです。
試しに以下のようにしてもエラーは解決されませんでした。
find . -type f | xargs sed -i "s/ユ.+ザ/User/g"
どうすれば全てのマルチバイトを正常に置換できますでしょうか?
ファイルの文字コードはSJISです。
解決に至った回答のみポイントを付与させていただきます。
宜しくお願い致します。
コマンド自体(特にsed)がマルチバイト対応の実装であることを前提としても尚、ファイル名のロケールとファイルの内容のロケールがズレている状態は、基本的に全てのコマンドが不得意とする領域です。よほど注意して扱わない限り危険は残ります。
まず、シェルスクリプト(あるいはコマンドライン)のロケールはファイル名のロケールに一致しているので、そのままでは標的ファイルの内容のロケールであるところのsedスクリプトは書けません。たまに動くことがあるのはまぐれです。どれだけクォートしても安全にはなりません。確実を期すならsedスクリプトは全て単体の別ファイルに保存するべきです。
どうしてもシェルスクリプトのインラインで済ませたいなら、ものすごく気持ち悪いですが、その場でiconvするくらいしか逃げ道はありません。
#!/bin/sh iconv -t SHIFT_JIS << @EOF | find . -type f -exec env LC_CTYPE=ja_JP.SHIFT_JIS sed -i -f - -- '{}' '+' s/ほげ/もげ/g @EOF
この場合にはシェルスクリプトは SHIFT_JIS ではなくファイル名の (つまりシステム標準の) ロケールで保存します。
しかしこうすると、今度はsedから見てファイル名のロケールが狂っているのです。つまり標的のファイル名にマルチバイト文字があった場合には、うまく動かないケースがあり得ます。これを回避するには、ファイルをパイプ経由で引き渡すようにしてファイル名をsedに見せなくするか、あるいは一時的にファイルを安全な名前にリネームしてから引き渡すか、そういうラッピングをしなければなりません。
なお今時の大半のシステムでは使用するロケール(ここではja_JP.SHIFT_JIS)のデータを事前生成する必要がある点に注意してください。この作法はOSによって異なります。例えばDebian系なら dpkg-reconfigure locales とか。
なおロケールとは別件として、
Linux 上で Shift_JIS を使う事がそもそもの原因でしょう。
16進で直接指定してはいかが?