Find (UNIX)

ここでは主にUnix系プラットフォームで利用できるディレクトリ検索プログラムであるfindについて記述する。ファイルシステムの1つ以上のディレクトリツリー上で検索を行い、ユーザーの指定した基準にマッチするファイルを探す。既定の動作としては現在のワーキングディレクトリ配下にある全ファイルをリストアップする。さらに find はマッチした各ファイルに対して何らかのアクションを実行するよう指定できるため、大量のファイルを操作することができる非常に強力なプログラムであるといえる。正規表現によるマッチングもサポートしている。

1. find における正規表現文法について
----------------------

find は評価式「-regex」や「-iregex」などを用いることで正規表現によるパス名のマッチングを行うことが可能であるが、GNUプロジェクト版のfindは複数の正規表現文法に対応している。そこで、GNU findは、既定では正規表現をGNU Emacsの正規表現として解釈し、他の異なる文法についてはコマンドラインオプション「-regextype」の引数によって解釈を切り替えて対応している。

2. 例
----

カレントディレクトリでの検索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// ディレクトリ配下にある通常ファイルの中から、//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 \ -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-- であり、ファイル所有者は完全なアクセスが可能だが、それ以外のユーザーは読むことしかできない。xargsとの連携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

/dev/null は、ファイル名を表示するために必要となる。そうしないと、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

3. Windows/MS-DOSコマンドとしてのfind
-----------------------------

Windowsコマンドとしてのfindは、ちょうどUnixコマンドでいうところのfgrepのような文字列検索を行う。実行すると検索文字列が含まれる行が標準出力に出力される。ちなみにgrepに相当するコマンドはfindstrである。

4. 外部リンク
--------

[1] JM Project
[2] man page
[3] man page
[4]