あるプロジェクトの中で、関数・変数がどこに書かれているか調べたいと言われた。やり方は色々あるけれども、レガシーにどの 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
xargs
は find
で見つけたファイルを一つ一つ 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 で」と検索できる程度には頭の隅に置いておきたいところ。
昔は、単純な検索で egrep が fgrep や grep よりも速いなんてことがよくありました……
ReplyDeleteへえ、そうなんですか。初耳です。面白い情報ありがとうございます。
Delete