@kyanny's blog

My thoughts, my life. Views/opinions are my own.

grep -q が速いワケ

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 で事前調査してマッチしたやつだけ詳細にパースする・・・とか考えたけど、結局マッチしなかった場合ってのはファイルのおしりまで読んじゃうわけなので意味ないな。。