普段から使ってるコマンドのうち、 一度覚えてしまったがために他の使い方を覚えられないコマンドの1つ。 前回の Vimで全体での文字列置き換え と同じような話。 ついでにfindについてちょっとしたまとめ。
findコマンド
特定のディレクトリ以下で特定の名前のファイルやディレクトリを探すコマンド。
$ find .
とすれば、カレントディレクトリ以下の全てのファイルとディレクトリが表示されます。
ファイル名を指定して検索
恐らく一番最初に覚えるものだと思いますが、-nameを使うと
指定された名前にマッチするファイル/ディレクトリだけを表示します。
$ find . -name "*.md"
*などのシェルスクリプトで使うような正規表現も一部使えます。
文字だけなら"で囲う必要は無いですが、正規表現を使ったりしたい場合は
囲う必要があります。
-nameを-inameとすると大文字小文字の区別をしなくなります。
なんですが、何故かこれを覚える前に、
$ find . | grep "md"
みたいにしてしまうことを覚えてずっと-nameを使ってなかったので
いい加減覚えて正しく使おう、と言うのが今回の件。
複数の名前を指定
-nameを使って名前を指定する時、複数の名前を指定するときは
$ find . -name "*.md" -o -name "*.txt"
の様に、各条件を-o(-or)または-a(-and)でつなげます。
さらに複雑な指定をしたい時なんかは\(/\)で括弧で囲って
優先順位を決めたりも出来ます。
指定の名前を除外
否定(-not)を使えば特定の名前以外を検索できます。
$ find . -not -name "test.*" -a -name "*txt"
で.txtという拡張子を持つファイルのうちtest.txt(もしくtest.aaa.txt等)以外の物を検索します。
パイプを使ってxargsで結果を渡す
findを使って良く行うのが検索結果を対象に何かコマンドを実行すること。
$ find . -name "*txt" | xargs ls
とすればfindで見つかったものに対してlsを行います。
ただ、これだと結果に出るファイルやディレクトリに空白があったりすると
その空白で区切った物がそれぞれ1つとしてlsの引数に渡されてしまいます。
試しに、空白のあるファイルを作って
$ vim "a b.sh"
この中身をこんな感じにします。
| 1 2 3 4 |  | 
このファイルがあるディレクトリでfindを実行してxargsでこのスクリプトに渡してみると
$ find . | xargs ./a\ b.sh
$1=.
$2=./a
$3=b.sh
$4=
こんな感じでaとb.shの部分が分離してしまいます。
これを避けるためには、findへ-print0と言うオプションを渡し
xargsに-0というオプションを渡します。
$ find . -print0 | xargs -0 ./a\ b.sh
$1=.
$2=./a b.sh
$3=
$4=
-print0でfindの結果をNULLで区切る様になります。
xargsの方の-0はインプットの区切りをNULL文字とします(デフォルトは空白と改行)。
-execでコマンドを実行
パイプ+xargsを使わずに、-execオプションを使うと
findの引数として直接見つかったファイルを
コマンドに渡す事も出来ます。
$ find . -exec ./a\ b.sh {} +
$1=.
$2=./a b.sh
$3=
$4=
-execに続いてコマンドを書いて、その後の{}の部分が
findで見つかったファイルに展開されます。
ここで、最後の+が、見つかったファイルを一括でコマンドに渡す、
つまり、上のパイプ+xargsと同等の状況にするようにするためのオプションです。
これを最後\;とすると、
$ find . -exec ./a\ b.sh {} \;
$1=.
$2=
$3=
$4=
$1=./a b.sh
$2=
$3=
$4=
の様になります。これは、見つかった物一つ一つをa b.shへ渡して
ファイルの数だけコマンドを実行します。
