みんな大好き grep についてですが、かなり機能が豊富な一方でなかなかそれがまとまっていないこともあるのと、自分自身、結構細かな Tips を忘れたりするので、ここで簡単にまとめてみたいと思います。
OR 検索
後述する拡張正規表現でも可能といえば可能ですが、サクっとできるのは -e
(--regexp
) を複数与えることです。
$ grep -e abc -e xyz hoge.txt abcdefg vwxyz
この OR 条件がときどき変わって、その都度シェルスクリプトを変えたくないという場合、これらの条件は外出しすることも検討して良いでしょう。外出ししたファイルには、条件を 1 行 1 条件で並べます。
このファイルを -f
(--file=filename
) で与えれば、grep はそれらを OR 条件として解釈します。
$ cat cond.dat abc xyz $ grep -f cond.dat hoge.txt abcdefg vwxyz
行全体ではなくて、特定のパターンに合致する部分だけ抽出して表示する
たとえば テキストファイルの中の IP アドレスだけ抜き出したいときとか。
具体例として、こんなゴミみたいなファイルがあるとします。 この中から IP アドレスだけ出してくれというゴミみたいな依頼がある場合があり、このような作業で時間を浪費するには人生は短すぎます。
$ cat ipaddr.txt jdisa;eoiuraajf dsaea dfa;eiue127.0.0.1da324 dsai;255.255.255.240dsa;ewapa3242 324632hogesada192.168.3.0;safdsa 0.0.0.0dsaafs10.252525.0.0.dsadfsa
こういうときには、-o
(--only-matching
) オプションを使います。合致したところだけが標準出力に出力されるので、「検索」ではなく「抽出」に使用できます。
ちなみに、以下の例では拡張正規表現 (-E
) を使っています。拡張正規表現については、別段 grep 限定の話でもないので、雰囲気で感じてもらえれば良いと思います。
$ grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" ipaddr.txt 127.0.0.1 255.255.255.240 192.168.3.0 0.0.0.0
ちなみに、これを利用すると以下のようなことも可能になります。
grep さん、そのファイルはバイナリではなくテキストなんやで…
バイナリファイルに対して grep
をかけても、grep
さんは 「マッチしたよ」という情報しか教えてくれません。
$ grep . app/WEB-INF/lib/jackson-dataformat-yaml-2.8.6.jar Binary file app/WEB-INF/lib/jackson-dataformat-yaml-2.8.6.jar matches
ホントにバイナリなら良いんですが、ときどきテキストファイルであるにも関わらず、バイナリと判定してしまうケースがあります。
この場合、テキストファイルであると、明示的に教えてあげると良いでしょう。-a
(--text
) オプションで指定できます。
今回のファイルは普通にバイナリファイルなので、ちょっとおかしくなっちゃいましたが。
$ grep -a . app/WEB-INF/lib/jackson-dataformat-yaml-2.8.6.jar | head -3 _tjr @&&h4ci8hv%PcON miwdMW8@ݤ- Hl&?JDi&J#rdhu߿.gQK> \4Mh̉FMcpݎzI<b_m.k[cj49%8"bɊ<ZgBt9WWw_r*]=A8TPj"1kv'' tm"hSwa]׳/1oJ
リアルタイムに tail -f
を grep
したいんだけど
grep はバッファリングした後出力するのがデフォルトなので、表示するまでには若干の時間がかかります。
これをリアルタイムに行うには、--line-buffered
を付与しましょう。
$ tail -f access.log | grep --line-buffered ERROR
合致する行の前後のn
行を表示する
システム運用やエラー調査でよく使ったりします。特定のエラーログの直前に出力されたログの傾向から大まかな対応方針が得られることが多々あります。
前の行を num
行表示するときは -B num
(--before-context=num
)です。
$ grep -iB2 QRS hoge.txt abcdefg hijklmn opqrstu -- ABCDEFG HIJKLMN OPQRSTU
後の行を num
行表示するためには -A num
(--after-context=num
)、前後の num
行を表示するためには -C num
(--context=num
) が使用できます。
ちなみに、上記のとおり「複数箇所」がヒットする場合の区切りには --
が使用されますが、これは --group-separator
オプションで変更が可能です。
grep -i -A2 --group-separator='=====' QRS hoge.txt opqrstu vwxyz ABCDEFG ===== OPQRSTU VWXYZ
grep に時間をかけたくない
1 件でも検索結果に合致すれば grep を打ち切りたいときがあります。
しかし、特に command | grep ...
というようにパイプの後段に grep
を配置する場合、command
の実行が遅いと grep
も終わりません。
こういうときは、合致する行が num
行になったら処理を打ち切るという -m
(--max-count=num
) を使います。
このあたりについては、以下のエントリが詳しいです。
netstat | grep が遅いなぁと思ったときにはgrep -m - As a Futurist...
word 検索をしたい
要するに、hoge
を検索するとき、abc hoge fuga
にはヒットさせたいけど、abchogefuga
にはヒットさせたくない、という要件。
これは、-w
(--word-regexp
) オプションを使います。
どこかで書いたと思ったら、ここだった。
大文字と小文字を区別しない
このあたりは知名度が高いオプションですね。-i
あるいは --ignore-case
のオプション付与で実現できます。いわゆる case-insensitive な検索をしたいときに使います。
$ grep -i FgH a.txt defghijkgrep
合致「する」行数を数える
合致した行ではなく、合致した行数だけ知りたいときには、-c
あるいは --count
を使います。なんかシェルのワンライナーを作るときとか、使ったりするときもあるのではないでしょうか。
$ grep -ic FgH a.txt 1
合致「しない」行を抽出する
-v
あるいは --invert-match
オプションを使います。
ps -ef | grep -v grep
とかのパターンでよく見かけます。もちろん、-c
と組み合わせれば、合致しない行数も知ることができます。
$ grep -icv FgH a.txt 3
合致する行が「ある」ファイル名を知る
-l
あるいは --files-with-matches
オプションを使うことで、ファイル名のみを出力することができます。
たとえば、/etc/pam.d から password
を含むファイル名を一覧するときは、以下のように使えば良いです。
$ find /etc/pam.d -type f | xargs grep -l password | head -3 /etc/pam.d/other /etc/pam.d/passwd /etc/pam.d/su
合致する行が「ない」ファイル名を知る
-L
あるいは --files-without-match
オプションを使えば良いでしょう。
$ find /etc/pam.d -type f | xargs grep -L password /etc/pam.d/config-util /etc/pam.d/runuser /etc/pam.d/runuser-l
ディレクトリを grep
単体で再帰検索する
上記では、/etc/pam.d
配下のファイルを find
を使って取得していますが、grep
単独でもこの再帰検索をサポートします。
多くの場合、これは -r
オプション (--recursive
) で実現しますが、実はこれ、--directories=recurse
でも実現できます。
$ grep -rl pam_unix /etc/pam.d /etc/pam.d/smartcard-auth /etc/pam.d/system-auth /etc/pam.d/fingerprint-auth /etc/pam.d/runuser /etc/pam.d/password-auth
$ grep -c --directories=recurse password /etc/pam.d | head -3 /etc/pam.d/other:1 /etc/pam.d/passwd:2 /etc/pam.d/su:1
また、-r
ではシンボリックリンクを追ってくれませんが、-R
(--dereference-recursive
) を使うと、シンボリックリンクを追ってくれるようになります。使ったことないけどな。
この再帰検索において、対象から除外したいファイルやディレクトリも指定できます。ファイルの場合は --exclude=glob
、ディレクトリの場合は --exclude-dir=glob
という形式です。
たとえばここでは、auth
で終わるファイルは除外しましょう。
$ grep -rl pam_unix /etc/pam.d /etc/pam.d/smartcard-auth /etc/pam.d/system-auth /etc/pam.d/fingerprint-auth /etc/pam.d/runuser /etc/pam.d/password-auth $ grep -rl pam_unix --exclude '*auth' /etc/pam.d /etc/pam.d/runuser