2013-06-13

find + grep でディレクトリー下の関数・変数を探す

あるプロジェクトの中で、関数・変数がどこに書かれているか調べたいと言われた。やり方は色々あるけれども、レガシーにどの Linux でも入っていそうな find と grep コマンドの組み合わせで関数・変数を探してみる。

1. find + grep

教科書的によく見かけるのが次の形。

$ find /path/to/dir -type f -exec grep function_foo {} \;

-type f でファイルのみ検索している (ディレクトリーの除外)。grep はディレクトリーを検索するとエラーを返すから。-exec COMMAND {}{} の中に find で見つかったファイル名が入る。-exec の終わりを知らせる為に ; を使う。ただし、普通に使うと shell に ; を解釈されてしまうので \; としてやる。最後に {}\; の間にはスペースを入れる (ここでハマッた)。

2. xargs

-exec を使うと見つかったファイル名 (のリスト) が一気に grep に渡される。プロジェクトが大きくなると、ファイル名が一万個以上になり、「shell の取れる引数の数の最大値」を越える。多少スピードは犠牲になるけれども、xargs コマンドを使うとこの問題を解決できる。

$ find /path/to/dir -type f -print | xargs grep function_foo

xargsfind で見つけたファイルを一つ一つ grep コマンドに渡している。

3. -print0

UNIX 由来のファイル名に徹しているなら幸せだけど、ファイル名に空白が入っていたりすると困ったことになる。-print の代わりに -print0 を使うと、そういう困ったちゃんのファイル名をちゃんとエスケープしてくれる。

$ find /path/to/dir -type f -print0 | xargs grep function_foo

4. fgrep

探すコードが正規表現を含まないのであれば... つまり、文字列を検索するのであれば grep コマンドより fgrep コマンドの方が少し高速。fgrep は正規表現に対応しない分だけ少し速い。

$ find /path/to/dir -type f -print0 | xargs fgrep function_foo

5. Emacs 連携

find と grep を組み合わせると、なかなかコマンドが長くなる。Emacs には、find + grep を楽にするコマンドが用意されている。

M-x find-grep
Run find (like this): find . -type f -exec grep -nH -e  {} +

ありゃ、xargs じゃなくて -exec を使ってるのね。まあ、難しいこと外えずに grep に与けるキーワードだけ考えればいいので便利。

あとがき

優れた IDE なら関数や変数の検索・置換コマンドが専用に用意されている。etags や ctags を使って TAGS ファイルを作っておけば高速に関数の検索が出来る (Emacs/vi との相性も良い)。grep の代替になるコマンドも現れている。

自分の環境に合わせて使い分けるのが良いと思う。

find と grep を使うやり方は一見古臭いけど、柔軟性の高さは比類がない。例えば、関数ではなく「TODO」や「FIXME」といった文字列、ファイルに含まれているメール・アドレス、ファイルを更新時間で限定する etc. 例を挙げればキリがない。

覚えておけとは言わないけれど、困った時にそういえば「find と grep で」と検索できる程度には頭の隅に置いておきたいところ。

2 comments:

  1. 昔は、単純な検索で egrep が fgrep や grep よりも速いなんてことがよくありました……

    ReplyDelete
    Replies
    1. へえ、そうなんですか。初耳です。面白い情報ありがとうございます。

      Delete