Diff of UNIX/コマンド/検索/find


TITLE:find - ファイルの検索
#navi(UNIX/コマンド)


find コマンドは指定したディレクトリ階層以下のファイルを検索するコマンドです。例えば
 % find . -name '*.c'
とすると、カレントディレクトリ以下の拡張子 .c のファイルを検索し、一覧表示します。

 % find [path...] [option] [expression] [action]

#会津大学において /usr/bin/find よりも /usr/local/bin/find のほうがバージョンが新しいようなのでこちらの説明をします。

#contentsx

*オプション [#s8b9ce92]

オプション(option)は以下のものが指定可能です。
|-depth|ディレクトリ本体の前に,ディレクトリの内容を先に評価する|
|-follow |シンボリック・リンクの参照先を検索する|
|-xdev|他のファイル・システムにあるディレクトリは探索しない|
|-maxdepth <levels>|最大で <levels> の深さまで探索する|

会津大学でのホームディレクトリは実はシンボリックリンクなので、% find $HOME -follow とやらなければいけないのですが、これだとホームディレクトリ内のシンボリックリンクも展開してしまうので、上の例では % find $HOME/. のように /. を追加してシンボリックリンクであることを意識させない工夫をしています。
( % ls -ld $HOME と % ls -ld $HOME/. を比べてみてください。
lrwxrwxrwx のように左が l の場合シンボリックリンクです。)

判別式(expression)は以下のものが指定可能です。
n の部分は +n にすると n 以上、-n にすると n 以下、 n だけの場合 n に等しい
という意味になります。
|-name pattern|pattern が現在のファイル名と一致する場合、真。この pattern にワイルドカードを使う際は、シェルで展開させずに find コマンドに渡すために "pattern" のように引用符で囲むか  \ でエスケープさせる必要がある|
|-iname pattern|大文字小文字を区別しない名前検索|
|-atime [+-]n|n 日前にアクセスされているファイルを検索する|
|-ctime [+-]n|ステータスが n 日前に変更されているファイルを検索する|
|-mtime [+-]n|データが n 日前に変更されているファイルを検索する |
|-empty|空なファイルや中身のないディレクトリを検索する|
|-user uname |所有者がunameなファイルを検索する(IDの数値も指定可能)|
|-group gname|グループ名がgnameのファイルを検索する(ID番号も指定可)|
|-perm [+-=]mode |ファイルのアクセス権がmodeであるファイルを検索する。modeには8進数を用いることもできる。例えば +a=w のように + とすると、この条件のなかのいずれかにあてはまれば真となる。a とは ugo のことなので -rw-r--r-- のような許可でも u=w は当てはまるので真となる。-a=x のように - とすると、この条件すべてにマッチすれば真となる。この場合は -rwxr-xr-x のような許可ならば真となる。= または省略の場合はよりきつく、完全マッチした場合のみ真となる。|
|-type c |指定したファイル・タイプを検索する。c には&br;b ブロックデバイス&br;c キャラクタデバイス&br;d ディレクトリ&br;f ファイル&br;l シンボリックファイル&br;p 名前付きパイプ&br;s ソケット&br;を指定する|
|-size [+-]n[bckw] |nのサイズのファイルを検索する。nの後にcを付加すると単位がバイトに,kを付加するとKバイトになる。何も付けないとブロック(通常は1ブロック=512バイト)になる|
|-inum n|inode 番号 n のファイルを検索する|
|-newer file|file よりタイムスタンプが新しいファイルを検索する|
また、さらに判別式中の operator が以下のようになっています。
|( expr ) |カッコのなかを優先的に判別する|
|-not expr |exprが異なる場合,検索対象となる。! expr も同じ|
|expr1 -and expr2 |expr1とexpr2をandで評価する。expr1 -a expr2 も同じ。また expr1 expr2 も同じ|
|expr1 -or expr2 |expr1とexpr2をorで評価する。expr1 -o expr2 も同じ|

アクション(action)は以下のものが指定可能です。
|-print |検索結果を標準出力する。このとき結果をフルパスで表示する。デフォルトなので省略可|
|-fprint file |検索結果をfileに書き出す。同名のファイルがある場合は上書きをする|
|-exec command \; |検索後,commandを実行する。このとき {} が検索されたファイル名におきかわる。; までがコマンドになる。このとき、シェルの ; にならないように \; のようにエスケープシーケンスを使用する必要がある。|
|-ok command \; |-execと同様に検索後commandを実行する。ただし,ユーザーに問い合わせる|
|-ls |結果をファイル詳細付きで表示する。"ls -dils"と同様な形式を指定する|
*使い方例 [#k80259fe]

**ファイル検索 [#s522f2a3]

 % find . -name 'test.txt'
とすると、カレントディレクトリ以下の test.txt という名前のファイルを検索します。
サブディレクトリも再帰的に辿って検索します。

ワイルドカードも使えます。
 % find . -name '*.txt'
とすると拡張子 .txt のファイルを検索し、一覧表示します。
シェルにワイルドカードを展開されてしまわないように、 ' (シングルクォーテーション) で挟んでおくのを習慣づけておくと良いです。

一覧表示のファイル名をダブルクォーテーションで囲んで表示するには次のようにします。
 %  find . -name '*txt' -exec echo \"\{\}\" \;
通常のファイル一覧では、
 ./Hoge hoge.txt
 ./hoge.txt
のように表示されるのが、
 "./Hoge hoge.txt"
 "./hoge.txt"
のようにダブルクォーテーションに挟まれて表示されるようになります。
**コマンド実行 [#l26a73f5]

例えば、
 % find $HOME/. -name "#*" -or -name ".*~" -or -name "*~" -exec rm {} \;
とすると、ホームディレクトリ以下の # で始まるファイルや後ろに ~ のつくファイル
(emacs の temp ファイル)を削除します。

しかし -exec オプションでは1つのファイルに対してコマンドを実行するため、1つのファイルに対して1つの rm プロセスを起動することになり、時間がかかるため(プロセス作成はかなりの負荷なのです)、
 % find $HOME/.  -name "#*" -or -name ".*~" -or -name "*~" | xargs rm
のようにしたほうが良いです。途中でキャンセルもできますし。
[[xargs]] というコマンドは入力を引数のコマンドの引数として渡すコマンドです。

なので、-exec や -ok はあまり使う必要がありません。ファイル1つ1つに個別に処理をしなければならないような時ぐらいです。

**複数ファイル内検索 [#p0c56831]

ファイル名を検索しつつ、ファイル中の文字列を検索するには
 % find . -name "[file_pattern]" | xargs grep [pattern]
のようにします。grep のオプション -n をつければ行番号もでるので検索にはそのほうがよいかもしれません。

grep のオプション -l をつけると、マッチした行の表示ではなく、マッチした行のあるファイルのファイル名を表示します。
 % find . -name "[file_pattern]" | xargs grep -l [pattern]

検索結果のファイル名に空白文字などが含まれていると、xargs がファイルをうまく認識してくれず、No such file とエラーがでてしまったりします。そういう時は
 % find . -name "[file_pattern]" -print0 | xargs -0 grep -l [pattern]
のようにします。find -print0 でファイル名の区切り文字が、改行文字からNULL文字(\0)に変わります。
そして、xargs -0 オプションで xargs が認識するファイル名の区切り文字をNULL文字(\0)に限定して処理させます。
これにより、ファイル名に空白文字が含まれていても問題なく処理させることができるようになります。
これにより、ファイル名に空白文字が含まれていても問題なく処理できるようになります。
**複数ファイル内文字列置換 [#eaf40a1c]

ファイル名を検索しつつ、ファイル中の文字列を置換するには、
 % find [path] -type f -name '[file_pattern]' | xargs grep -l '[対象文字列]' | xargs sed -i.bak 's/[対象文字列]/[置換後文字列]/g'
のようにするとよさそうです。
sed -i でファイル内文字列を置換、上書きします。-i.bak としておけば .bak 拡張子のついたファイルにバックアップをとっておいてくれます。
**特定ファイルを除いてファイル名検索 [#ud9bed03]

ある特定のファイル(ディレクトリ)を除いてファイルをリストするには
 % find . -name "[file_pattern]" -prune -o -print
とします。[[svn]] を使用している場合に .svn ディレクトリを無視して zip ファイルを作ったりするのに便利です。
 % find . -name ".svn" -prune -o -print | xargs zip a.zip

#navi(UNIX/コマンド,,footer)
xrea