SOFTELメモ Developer's blog

会社概要 ブログ 調査依頼 採用情報 ...
技術者募集中

【linux】findコマンドで「.svnディレクトリの中以外」

問題

findコマンドでファイルを検索するとき、.git ディレクトリや .svn ディレクトリや *.bak ファイルや *.log ファイルなどは対象外にしたい。

「*.phpファイル で .svnディレクトリの中のファイルではない」などを表現できる?

答え

findコマンドの書式を簡単に書くと、以下のとおり。

find オプション 探す場所 式 式 式 ...

後ろに続くものはすべて式。

式は判定条件(-name, -type, -mtime …)であったり、アクション系のオプション(-print、-delete、-exec …)であったり、動作を変更するオプション(-depth …)であったりするが、すべて戻り値がある。

プログラムのif文の条件のように、「式 and 式 and 式 and …」なら、左から各式が真を返す限り評価される。or(-o)結合で「式 or 式 or 式 or …」なら、左からいずれかの式で真が返ってくるまで評価される。

先に評価したい式があれば、計算式のように括弧()で囲む。括弧はシェルにとって特別な意味を持っているので、クォートする必要がある。\( ...... \) となる。

-printを省略しても、検索したファイルを画面に表示してくれる環境が多いが、その動作は保障されているわけではないので、-print を書く癖を付けておいて損はしない。ちなみに -printは常に真を返す。

以上を把握していれば、以下の例が理解可能だと思う。

用例

あるディレクトリ以下のファイルを表示する。

$ find /tmp/test -print
/tmp/test
/tmp/test/test1
/tmp/test/test1/test2.php
/tmp/test/test1/test.txt
/tmp/test/test1/test.html
/tmp/test/test1/test.php

あるディレクトリ以下から、末尾が.phpの名前のファイルを見つけて表示する。

$ find /tmp/test -name "*.php" -print
/tmp/test/test1/test2.php
/tmp/test/test1/test.php

あるディレクトリ以下から、末尾が.phpの名前のファイルを表示して、表示する(なんの役に立つのかは別として)。

$ find /tmp/test -name "*.php" -print -print
/tmp/test/test1/test2.php
/tmp/test/test1/test2.php
/tmp/test/test1/test.php
/tmp/test/test1/test.php

あるディレクトリ以下から、末尾が.phpの名前のファイルを削除する。その後の状態を確認する。

$ find /tmp/test -name "*.php" -delete
$ find /tmp/test -print
/tmp/test
/tmp/test/test1
/tmp/test/test1/test.txt
/tmp/test/test1/test.html

あるディレクトリ以下から、末尾が.phpの名前のファイルを表示してから削除する。削除したファイルを確認できる。ディレクトリも空であれば-deleteで削除できる。

$ find /tmp/test -name "*.php" -print -delete
/tmp/test/test1/test2.php
/tmp/test/test1/test.php

and結合の方がor結合より強いので、以下のように書くと、(-name “*.php” -print) と (-name “*.html” -print) が先に評価される。拡張子がphpのファイルと拡張子がhtmlのファイルがいずれも表示される。

$ find /tmp/test -name "*.php" -print -o  -name "*.html" -print
/tmp/test/test1/test2.php
/tmp/test/test1/test.html
/tmp/test/test1/test.php

拡張子がphpのファイルと拡張子がhtmlのファイルを表示したいときに以下のように書くと、(-name “*.php”) と (-name “*.html” -print) が先に評価されるので、表示されるのはhtmlファイルのみとなる。

$ find /tmp/test -name "*.php" -o  -name "*.html" -print
/tmp/test/test1/test.html

拡張子がphpのファイルと拡張子がhtmlのファイルを表示したいときの別な書き方はこう。括弧は演算子なので、括弧の前後にスペースは必要。

$ find /tmp/test \( -name "*.php" -o  -name "*.html" \) -print
/tmp/test/test1/test2.php
/tmp/test/test1/test.html
/tmp/test/test1/test.php

問題の「ファイルで、パス中に .svnを含まないものを表示する」のは、以下のように書ける。否定は !だが、これもシェルにとって意味がある文字なのでエスケープして \!と書く。

$ find . -type f \! -path '*.svn*'

条件をちょっと変えて、ここ30日変更がないファイルを表示する例。

$ find . -mtime +30 -print

and と or の動きを見る例

and結合だと、falseが登場した時点で評価が終了するため -printが評価されない → 何も表示されない。

$ find . -false -print

or結合だと、trueが登場した時点で評価が終了するため -printが評価されない → 何も表示されない。

$ find . -false -o -true -o -print

全部表示される(find . -print となんら変わりない)。

$ find . -false -true -o -true -false -o -print

最初に -print(アクション系だが真を返す)があると、後ろに何があっても、とりあえずすべてのファイルが表示がされる。

$ find . -print -false -true -false

参考

http://linuxjm.sourceforge.jp/html/GNU_findutils/man1/find.1.html

関連するメモ

コメント