特定ディレクトリ以下を除いた find をする方法
find . -name "foo" -prune -o -print
同僚から、あるディレクトリ配下で特定ディレクトリ配下を排除したファイルリストが欲しい、何か方法はない? と訊かれた。find の !
使えば良いじゃんと思ったが、前提として環境はAIX*1だ。-path
オプションがない。-path
オプションがあったとしても、find . ! -path "*/foo*"
では排除したくないfoobarディレクトリも排除してしまう。
悩みつつはじき出したのがこれ。
まぁ悩んだと言っても実はManpage of FINDの-path
の項に書いてあったのを使っただけだが。ただ、上記例でうまくいく理由を考えることででfindの奥深さを味わった。思わぬ課題を出してくれた同僚に感謝。
findの奥深さ
-prune
というのは条件に一致したディレクトリ配下は検索しないオプションだ。ただし、そのディレクトリ(上記例ではfooディレクトリ)自体が排除されるわけではない。
find . -name "foo" -prune
を実行するとfooディレクトリとファイル*2のみのリストとなる。
では、find . -name "foo" -prune -o -print
の場合、fooディレクトリとそのディレクトリ配下以外のファイルと-name "foo"
の条件に偽となったもののリストとして表示される様に思える。が、しかし、そうはならない。fooディレクトリとその配下を排除したリストとなる(つまりfooディレクトリが表示されない)。
なぜか。
man find の冒頭にある
Manpage of FIND
- prune 以外のアクションが評価式に含まれていない場合は、評価式の結果が真となったファイルに対して -print が実行される。
が答えだ。
find . -name "foo"
というコマンドで上記を考えると、アクションに属するオプション指定が無いので内部的にはfind . -name "foo" -print
となる。逆に-print
等のアクションの指定がなければ、評価が真であっても出力されないと考えられる。
もう一度find . -name "foo" -prune -o -print
を見ると-print
が明示されている。よって、-name "foo" -prune
には暗黙的な-print
が付加されず、出力されない。
find . -name "foo" -prune -o -name "*"
だと、暗黙的に-print
が付加されて、fooディレクトリも表示される。
ふぅ、言葉にすると長ったらしいな。
まとめ
- アクション指定がない場合は
-print
が暗黙的に付加される - 特定ディレクトリとその配下を排除したい場合は
find . \( -name .svn -o -name .CVS \) -prune -o -print
でOK
余談
300ものブックマークが付いているけど、コマンド例に若干の不安が。。。
できることならxargsを使え
find /tmp -type f -exec rm -f {} \;
じゃなくて
find /tmp -type f -print0 | xargs -0 /usr/bin/rm