[linux] grepでファイル単位の検索条件

2017年5月29日月曜日

grep linux

grepを使うときは基本的に1行の中のパターンを検索するし、たいていの場合はそれで問題ない。
でも、たまに条件に合致するファイルを抽出する必要がでてきたりするよね。
行単位ではなく、ファイル単位での検索条件を考えてみよう。

準備

今回、試したのはGNU grep。
また、確認用のファイルとしてtest1,test2,test3を用意する。
$ grep --version
grep (GNU grep) 2.16
Copyright (C) 2014 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

作者 Mike Haertel および その他の方々は <http://git.sv.gnu.org/cgit/grep.git/tree/AUTHORS> を参照してください。
$ cat test1
green
yellow
red
$ cat test2
grape
apple
orange
$ cat test3
apple
red
$

あるキーワードが1回でも出現するファイルを抽出する

ファイル単位での検索の場合、結果は条件に合致したファイル名を表示するということになる。
そういう場合は"-l"オプションを使う。
$ grep -l -F -e 'apple' *
test2
test3
$

'apple'をキーワードとして持つ、test2,test3が表示される。

2つ以上のキーワードのいずれかを含むファイルを抽出する

別の行でいいので、ファイル中に2つ以上のキーワードのいずれかを含むという検索条件ではどうだろうか。
"-e"オプションを使えば、検索条件を複数指定できるので、それを使おう。

'orange'か'red'を含むファイルを抽出すると。
$ grep -l -F -e 'orange' -e 'red' *
test1
test2
test3
$

test1,test2,test3のすべてが表示された。

2つ以上のキーワードをすべて含むファイルを抽出する

今度は複数の検索条件のすべてに合致するファイルを探そう。
このためにはgrepをパイプでつないで、最初の検索結果をxargsで受け取って、前段で抽出したファイルのみをさらにgrepすればいい。

'apple'と'orange'を両方含むファイルを抽出すると。
$ grep -l -F -e 'apple' * | xargs grep -l -F -e 'orange'
test2
$

こうすると、最初のgrepでtest2,test3が抽出されて、2つめのgrepではtest2,test3のみが検索対象となる。

あるキーワードが指定回数以上出現するファイルを抽出する

"-c"オプションを使うと、ファイル名の後ろにマッチした回数が表示される。
また、"-m"オプションを使うと、マッチ回数の上限を指定できる。
これを組み合わせることによって、キーワードが任意の回数以上出現するファイルのみを抽出することが出来る。
$ grep -c -F -e 'a' *
test1:0
test2:3
test3:1
$ grep -c -m2 -F -e 'a' *
test1:0
test2:2
test3:1
$ grep -c -m2 -F -e 'a' * | grep -e ':2$'
test2:2
$

ヘッダを除去する。ヘッダ以降のみを検索する

"-m"オプションはマッチ回数の上限を指定できるだけではなく、ファイルをリダイレクトして、標準入力としてgrepに渡すと、マッチした行の直後に入力ストリームの位置をセットして、終了してくれる。
ファイル単位の検索という話からはずれてしまうけど、これの応用としてはヘッダの終了までは読み飛ばすような処理が考えられる。

例えば、"-"が連続するような行が出現するまではヘッダとみなされるファイルのヘッダ以降のみを抽出したければ、以下のようになる。
$ cat fruits.txt
name   amount
         0-99
------ ------
grape       5
orange      7
banana     99
$ { grep -m1 -G -e '^-----' >/dev/null; cat; }<fruits.txt
grape       5
orange      7
banana     99
$ { grep -m1 -G -e '^-----' >/dev/null; grep -F -e '99'; }<fruits.txt
banana     99
$

これで、ヘッダの"0-99"は無視されるようになったね。