1つのファイルに対して、沢山のパターン検索をしたい時に、grep -f pattern_fileでも良いのですが、ファイル内の全てのパターンを展開して実行してしまうみたいで、もの凄くメモリを食います。grep -e pattern1 -e pattern2 ・・・・-e patternXみたいな感じですね。
forループで1つのパターン毎にgrepを実行する方法もあります。
for i in `cat pattern_file`
do
grep -E $i target_file >> grep_result.txt
done
これだと1つのパターンに対して1回ずつgrepを起動するので、パターンの回数実行されます。CPUを沢山積んでいるマシンの場合には、勿体ないですね。
そこで、本題です。xargsを使って多重起動してgrepを実行します。実行するコマンドはgrepでなくても良いので応用が可能ですね。
多重起動して、結果を同じファイルに書き込むと場合によってはファイルが壊れる可能性があるので、念のためfifoを経由してファイルへ書きます。(何度か実行しても問題はなかったんですが)
手順としては、以下の感じです。
1. fifoを作る
2. fifoから読みだして、ファイルへ保存するコマンドをバックグラウンドで動かす(別ターミナルでやった方が良いかも)
3. xargsでgrepを動かす。
$ mkfifo myfifo
$ cat ./myfifo > grep_result.txt &
$ xargs -a pattern_file -P4 -L1 -d '\n' -I% sh -c "grep -E '%' target_file > ./myfifo"
- -P4: 多重度(CPUの数に合わせて適当に)
- -L1: 1回のgrepに渡す引数の数。改行で区切られている前提
- -d '\n': デリミタ、ここでは改行を指定しているい。これを指定する事により、ダブルクォートなどはそのまま渡される。逆にこれを指定しないとダブルクォートなどは取り除かれてしまうので、想定される結果が得られない事がある。
- -I%: 実行するコマンドの中で、引数が置き換えられる位置を指定する。ここでは%を指定して、grepコマンドの検索パターンの部分の%の位置で置き換える。よくあるのは{}ね。
ファイルをリダイレクトする場合は、shの引数としてリダイレクトも含めて実行するコマンドを指定する。
リダイレクト先にfifoを指定しているのは、同時に複数のコマンドで同じファイルへ書き込むと欠落したり、ファイルが壊れるのを防ぐためにfifoで排他制御してもらうため。
別に直接grep -E pattern target_file > grep_result.txtとかやっても問題なく動きはしたんだが、Unixの原理上壊れる事があるはずなので、念のため。
xargsの実行が終わると、バックグラウンドで動かしていたcatは自動的に終了する。
限られた時間内で作業しなくちゃいけなくて、CPUの数に余裕がある場合は、こんなやり方でやると、時間が大幅に短縮できます。forで1時間くらいかかったのが、xargsで4多重にしたら、10分程度で終わりました。その代わりCPUの負荷はかな~り高かったですけどね。