Find (UNIX)
ここでは主にUnix系プラットフォームで利用できるディレクトリ検索プログラムであるfind(ファインド)について記述する。の1つ以上のディレクトリツリー上で検索を行い、ユーザーの指定した基準にマッチするを探す。既定の動作としては現在のワーキングディレクトリ配下にある全ファイルをリストアップする。さらに find はマッチした各ファイルに対して何らかのアクションを実行するよう指定できるため、大量のファイルを操作することができる非常に強力なプログラムであるといえる。正規表現によるマッチングもサポートしている。
find は評価式「-regex」や「-iregex」などを用いることで正規表現によるパス名のマッチングを行うことが可能であるが、GNUプロジェクト版のfindは複数の正規表現文法に対応している。そこで、GNU findは、既定では正規表現をGNU Emacsの正規表現として解釈し、他の異なる文法についてはコマンドラインオプション「-regextype」の引数によって解釈を切り替えて対応している。
find . -name 'my*'
この例では、カレントディレクトリ(ピリオドで表されている)とその配下にあるファイルとディレクトリについて名前が「my」で始まるものを探す。クォートはfindに渡す前のシェルパターン「my*」をシェルが解釈して、カレントディレクトリにある my で始まるファイル名リストに置き換えてしまうのを防ぐために必要となる。
find . -name my\* -type f
この場合、上の例での検索結果から通常のファイルのみを絞り込む。従ってディレクトリやスペシャルファイルやパイプやシンボリックリンクなどは除外される。シェルパターン中で「*」の前にバックスラッシュを付けているのは、前の例と同じ理由を回避する別の書き方である。また、式は左から順に処理されるため、通常のファイルだけに絞り込んでから検索を行うには式の順序を並べ替えて「-type f -name my\*」とする。当然、結果は同じになる。
これまでの例は検索結果をリスト表示するだけであった。これは既定のアクションとして -print を実行するようになっているためである。なお、初期の find コマンドには既定のアクションが存在しなかったため、何も指定しないと検索をするだけでその結果は表示されなかった。
find . -type f -name 'my*' -ls
この場合、単にファイル名だけでなく、lsコマンド相当の情報を表示する。
find / -type f -iname myfile -print
この場合、そのコンピュータ上の通常ファイルから名前が「myfile」や「MyFile」などとなっているものをすべて検索する。「-iname」は大文字小文字を区別しない検索式。単にデータファイルを探すだけなら、「/」を指定するのは非常に時間がかかる可能性があり、賢い方法ではない。
find /home/brian -type f -name myfile -print
この場合、/home/brian ディレクトリ(ユーザー brian のホームディレクトリ)配下にある通常ファイルの中から、myfile という名前のものを探す。このように可能な限り範囲を狭めてコマンドを実行すべきである。
find local /tmp -type d -name mydir -print
この場合、検索範囲はカレントディレクトリにある「local」というサブディレクトリと「/tmp」ディレクトリである。その中からディレクトリのみを対象とし、名前が「mydir」であるもの探して表示する。
ユーザーがrootではない場合、アクセス権限などによるエラーを表示させたくない場合がある。その場合は、以下のようにしてシェルで標準エラー出力を捨てればよい。
find / -type f -name myfile -print 2> /dev/null
find . -type f \( -name '*jsp' -or -name \*java \) -ls
この例では名前が 'jsp' または 'java' で終わるファイルを探す。括弧が必要であることに注意。また、"or" を "o" と略記することも可能。大抵のシェルでは丸括弧を "\(" や "\)" のようにエスケープする必要がある(シェルの特殊文字として解釈されないようにするため)。-ls と -or は全てのバージョンの find で使えるわけではない。
find /var/ftp/mp3 -type f -iname '*.mp3' -exec chmod 744 {} \;
この場合、ディレクトリ /var/ftp/mp3 にあって名前が「.mp3」や「.MP3」などで終わるすべてのMP3ファイルについて、を変更する。アクションは -exec chmod 744 {} \; の部分で指定されている。MP3ファイルそれぞれについて、chmod 744 {} というコマンドの {} の部分をファイル名に置き換えたものが実行される。セミコロン(シェルが解釈するのを防ぐためエスケープされている)がコマンドの終わりを示している。なお、744 というパーミッションは普通に記せば rwxr--r-- であり、ファイル所有者は完全なアクセス(読み書きと実行)が可能だが、それ以外のユーザーは読むことしかできない。
find /var/ftp/mp3 -type f -iname '*.mp3' -print | xargs chmod 744
上の-execでは、該当するファイル1つにつき一度ずつプロセスを起動することとなり、効率が悪い。いったん-printで出力し、xargsによってコマンドラインとして渡せるだけの量をまとめて渡すことで、効率化することができる。なお、GNUプロジェクト版のfindには、ファイル名をヌル文字区切りで出力する-print0オプションがあり、xargs -0で解釈させることで、ホワイトスペースの入ったファイル名についても正常に処理可能となる。
以下の例では、/tmp 配下にある全ファイルについて、ファイルの中身に "search string" という文字列がないか検索するものである。
find /tmp -exec grep "search string" '{}' /dev/null \; -print
<tt>/dev/null</tt> は、ファイル名を表示するために必要となる。そうしないと、grepでマッチしたテキスト部分しか表示されない。等価な機能として grep の "-H" オプションや "--with-filename" オプションを使うことも出来る。
find /tmp -exec grep -H "search string" '{}' \; -print
GNU grep では、同等の処理を単独で以下のように行うこともできる。
grep -R /tmp "search string"
次の例は、jsmith というユーザーのホームディレクトリで "LOG" という文字列を検索したものである。
find ~jsmith -exec grep "LOG" '{}' /dev/null \; -print
/home/jsmith/scripts/errpt.sh:cp $LOG $FIXEDLOGNAME
/home/jsmith/scripts/errpt.sh:cat $LOG
/home/jsmith/scripts/title:USER=$LOGNAME
次の例では、カレントディレクトリ配下で全ての XML ファイルについて "ERROR" という文字列を検索する。
find . -type f -iname '*.xml' -exec grep "ERROR" '{}' \; -print
検索文字列を囲むダブルクオート (" ") やファイル名に対応する括弧を囲むシングルクオート (' ') はこの例では無くてもよいが、空白を含む文字列や特殊文字を含む文字列を指定する際に必要となる。
find . -user
Windowsコマンド(MS-DOSコマンド)としてのfindは、ちょうどUnixコマンドでいうところのfgrepのような文字列検索を行う。実行すると検索文字列が含まれる行が標準出力に出力される。ちなみにgrepに相当するコマンドはfindstrである。