読者です 読者をやめる 読者になる 読者になる

verbalize

文章書く練習.適当でいいから色々書きたい.

find | xargs grep を知る

はじめに

今回は頻繁につかうのに理解していないスクリプトがあるのでそれについて簡単に書こうと思います。そのスクリプトがコレです。

find . -name "*.py" -print0 | xargs -0 grep -i "pync"

どんなことをするスクリプトかご存知ですか?

これはカレントディレクトリ以下にあるファイル*.pyの中からpyncという単語を含むものをリストアップするワンライナーです。すなわちこういうことです。

find . -name "<検索対象のファイルネーム>" -print0 | xargs -0 grep -i "<探したい単語>"

前から何度か使っていたものの、たまにしか使わないしすぐ忘れるだろうということで、すぐにシェルスクリプトファイルで保存し「ブラックボックス化」していました。最近になって特に多用するようになってきたのでちゃんと理解しようと思います。

解読

まず、パイプの左側のfindの部分について。

find . -name "<検索対象のファイルネーム>" -print0

言わずもがなfind .でカレントディレクトリ以下の全てのディレクトリ・ファイルを列挙します。よくファイルを探す時に使いますね。

そして -name "*.<拡張子>"とすることで検索対象のファイルを限定します。ファイルをたくさん持つディレクトリを検索するのであれば時間を短縮できます。加えてこれを使わずに実行すると、サブディレクトリもリストアップされてしまい、パイプ以降に渡されるとエラーを起こします。結果が見づらくなるのでそれを防止する効果もありますね。

最後に-print0ですがこれは全く使ったことのないオプションだったのでmanで見てみると、

-print0
 This primary always evaluates to true. It prints the pathname of the current file to standard output, followed by an ASCII NUL character (character code 0).

とあります。簡単に訳すと「この引数は常に真と評価されます。ファイルのパスをASCIIのnull文字(0)を付けて標準出力に出力します。」という感じでしょうか。

f:id:bonhito:20161026000730p:plain

実際に↑のようなディレクトリで-print0を付けた場合とそうでない場合を比べてみます。

実行結果
-print0なし f:id:bonhito:20161026000830p:plain
-print0あり f:id:bonhito:20161026000836p:plain

-print0してもただつながってるようにしかみえませんが、hexdumpすると確認できます。

実行結果
-print0なし f:id:bonhito:20161026103848p:plain:w500
-print0あり f:id:bonhito:20161026103849p:plain:w500

そして後半のxargs以降。

xargs -0 grep -i "<探したい単語>"

xargsはこれも言わずもがなリダイレクトされた標準出力を次のコマンドの引数として渡すコマンドです。今回は次のgrepに渡します。ただ-0はなんだろうということでmanを見ると

-0      Change xargs to expect NUL (``\0'') characters as separators, instead of spaces and newlines.  This is expected to be used in concert with the -print0 function in find(1).

なるほど、find-print0とペアなのだとわかります。これは高速化のためでしょうか。調べてみると全く違いました。実際使うとうまくいかないので気づくのですが、xargsスペースまたは改行で区切って順にgrepの引数に渡してしまうので空白を含むファイルでNo such file or directoryのエラーがでてしまいます。

kaworu.jpn.org

最後にgrep -iですが、同様にmanを見ると

-i, --ignore-case
             Perform case insensitive matching.  By default, grep is case sensitive.

case insentive matchingとは大文字小文字を無視するということですね。

まとめ

当たり前ですが特別トリッキーなことはしていないので理解できて良かったです。大筋の流れとしてfindで列挙したファイル内の文字列に対してgrepを実行するのだなというのもより強くインプットできました。また、最後にgrepを実行するので最初に述べたようにディレクトリが渡されるのはまずいというわけですね。

ただそれでも毎回打つのはやはり面倒くさいので、結局

# find word
alias findword='find . -type f -print0 | xargs -0 grep -i $1'

と.zshrcに追記。実際のケースでは比較的小さな作業用ディレクトリを検索することが多いので-nameで限定はせずに-type fでファイルのみを全て出力させています。

めでたしめでたし👏🏼 。かなりざっくり書いたので間違いや表現に問題があれば気軽にコメントいただけると嬉しいです。

英語のお勉強メモ

単語 意味
evaluate to "expression A evaluates to value B" なら、「式 A は値 B として評価される」という意味。
followed by "HAPPEN AFTER" : to happen or do something after something else