grep(1) のオプションを試していたら、 grep -q PATTERN が grep PATTERN や grep -s PATTERN に比べて劇的に速い場合があるのに気づいた。気になったので少し追ってみた。環境は Mac OSX Leopard (10.5.8) で grep --version は grep (GNU grep) 2.5.1 です。
テスト用に適当なファイルを作って、オプションをかえつつ grep してみる。
kyanny-macbook:2009-12-01 kyanny$ perl -le 'print q{0} x 1000 for 1 .. 1_000_000' > hoge kyanny-macbook:2009-12-01 kyanny$ echo 1 >> hoge kyanny-macbook:2009-12-01 kyanny$ time grep -c 0 hoge 1000000 real 0m21.224s user 0m3.178s sys 0m1.120s kyanny-macbook:2009-12-01 kyanny$ time grep -s -c 0 hoge 1000000 real 0m22.163s user 0m3.324s sys 0m1.165s kyanny-macbook:2009-12-01 kyanny$ time grep -q -c 0 hoge real 0m0.134s user 0m0.001s sys 0m0.005s
- q だけ超速い。 man を読むと、
-q, --quiet, --silent Quiet; do not write anything to standard output. Exit immediately with zero status if any match is found, even if an error was detected. Also see the -s or --no-messages option. -s, --no-messages Suppress error messages about nonexistent or unreadable files. Portability note: unlike GNU grep, traditional grep did not conform to POSIX.2, because traditional grep lacked a -q option and its -s option behaved like GNU grep's -q option. Shell scripts intended to be portable to traditional grep should avoid both -q and -s and should redirect output to /dev/null instead.
ということで、よく読めば「マッチしたらすぐに exit(0) するよ」って書いてあるんだけど華麗に読み飛ばして grep.c を読みにいってしまい、まぁ当然ながらそのようになっていた。
http://www.opensource.apple.com/source/grep/grep-24/grep/src/grep.c
-q オプションが指定されてると exit_on_match というフラグがセットされて、 grepbuf() の while ループの中で exit(0) してると。
じゃあ PATTERN にマッチするものがファイルの先頭じゃなくて末尾にあったらどうなの?と少し意地悪なことをやってみると、
kyanny-macbook:2009-12-01 kyanny$ time grep -c 1 hoge 1 real 0m21.952s user 0m3.073s sys 0m1.163s kyanny-macbook:2009-12-01 kyanny$ time grep -s -c 1 hoge 1 real 0m22.261s user 0m3.099s sys 0m1.163s kyanny-macbook:2009-12-01 kyanny$ time grep -q -c 1 hoge real 0m22.373s user 0m3.101s sys 0m1.158s
まぁ予想どおり全部同じくらい時間がかかると。で結果が何もでてこない grep -q に使いどころなんてあんのかよって話だけど、存在確認さえできればいいケース(具体例は思いつかない)では $? をみればマッチしたかどうかだけはわかるので、大量のでかいログファイルから何行か探したいけど全ファイル調べるのはコストが高いので -q で事前調査してマッチしたやつだけ詳細にパースする・・・とか考えたけど、結局マッチしなかった場合ってのはファイルのおしりまで読んじゃうわけなので意味ないな。。