システムと異なる文字コードなファイルに対する grep

ってことで、記念すべき第 1 回目の技術ネタ!


私は Mac から Terminal.app 使って Xen の上に居る開発マシンで vim 使ってモリモリ開発してます。

普段は文字化けとか気にせずに開発出来るように設定してるので文字コードについてはそこまで意識してません。

しかし、大量のファイルのマルチバイトな文字列を一括で置換する必要が出てきたので、対象ファイルを抽出したいなぁとか思ったわけですわ。

具体的に言うと、UTF-8 なシステム上にある EUC-JP なファイルの部分文字列引っ掛けてリストアップしたい状況なわけですよ。

まぁ、真っ先に思いつくのが「grep すりゃええやん」ってことなんですが、「マルチバイトの grep って…?」ってことで色々試行錯誤したですよ。

結果として、かなりシンプルに出来ることが判明したので、覚書。


例えば、「カレントディレクトリ以下の全ての PHP ファイルのうち "ほげげげ" を含むファイル名を抽出」を実行する場合を考える。

まず試したのが、「普通にマルチバイト文字列を grep の pattern に突っ込む」。

zargs -- **/*.php -- grep -l 'ほげ\{3\}'

ま、無理だよね。知ってたよ。

あ、grep の -l オプションはファイル名だけ出力ってやつね。

次に試したのが、「LANG 変えて、Terminal.app のエンコーディングも変えて grep」。

export LANG=ja_JP.eucJP
zargs -- **/*.php -- grep -l 'ほげ\{3\}'

およ?巧く行かない…。

んで、自分の知識を過信するのをスッパリ諦めて、grep の usage 見てみる。

grep --help
使用法: grep [オプション]‥‥ パターン [ファイル] ‥‥
「ファイル」もしくは標準入力から「パターン」の検索を行なう。

(中略)

  -P, --perl-regexp         「パターン」をPerl正規表現とする
  -e, --regexp=パターン     正規表現に指定パターンを使う
  -f, --file=ファイル       「パターン」を指定ファイルから読む
  -i, --ignore-case         大文字/小文字を無視する
  -w, --word-regexp         「パターン」一致を単語全体(単語単位)で行なう

(中略)

バグは  へ報告してください。


ってことで、「-f オプション使って、予め EUC-JP で pettern を記述してあげれば良いじゃん」ってことに気付く。

echo 'ほげ\{3\}' | nkf -e > hoge.pat
zargs -- **/*.php -- grep -l -f hoge.pat

これで目的が果たせました!

ま、本当はもっと良い方法あるんだろうけど、私のスキルじゃこれが限界ってことで。