検索したファイルをまとめてコマンド実行したい
findコマンドで検索したファイルに対して、rmやchmodなどのコマンドを実行したい場合がある。findコマンドの-execオプションに{} +を指定すると、検索結果のファイルをまとめて1回のコマンド実行にまとめられる。
-exec {} ; はファイル1つにつき1回コマンドを実行する
{}はマッチしたファイルパスに置き換わるプレースホルダー。末尾を\;にすると、マッチしたファイル1つにつき1回コマンドを実行する。
$ find . -name '*.log' -exec echo {} \;
./app3.log
./app1.log
./app2.log
3つの.logファイルに対して、echoコマンドが3回実行されている。
-exec {} + はファイルをまとめて1回コマンドを実行する
末尾を+にすると、マッチしたファイルをすべて1つのコマンドの引数にまとめて、コマンドを1回だけ実行する。
$ find . -name '*.log' -exec echo {} +
./app3.log ./app1.log ./app2.log
3つの.logファイルが1回のechoコマンドの引数として渡されている。wc -lと組み合わせると、複数ファイルの行数をまとめて集計できる。
$ find . -name '*.log' -exec wc -l {} +
1 ./app3.log
1 ./app1.log
1 ./app2.log
3 total
コマンドの実行回数を比較する
実際にコマンドが呼び出される回数を、呼び出しをログに記録するスクリプトで確認する。
$ cat counter.sh
#!/bin/bash
echo "called with: $@" >> call.log
$ chmod +x counter.sh
$ find . -name '*.log' -exec ./counter.sh {} \;
$ cat call.log
called with: ./app3.log
called with: ./app1.log
called with: ./app2.log
$ rm call.log
$ find . -name '*.log' -exec ./counter.sh {} +
$ cat call.log
called with: ./app3.log ./app1.log ./app2.log
\;ではcounter.shが3回呼び出されているのに対し、+では1回にまとめて呼び出されている。対象ファイルが大量にある場合、+を使うとコマンドの起動回数を減らせるため、処理が速くなる。
{} はコマンドの末尾に1つだけ書く
+を使う場合、{}はコマンドの末尾に1つだけ書く必要がある。{}の後に別の引数を続けるとエラーになる。
$ find . -name '*.log' -exec echo {} extra +
find: missing argument to `-exec'
{}より前に固定の引数を置くことは可能。
$ find . -name '*.log' -exec echo before {} +
before ./app1.log ./app2.log
ファイル名にスペースを含む場合も安全に扱える
-exec {} +はファイル名をコマンドの引数として直接渡すため、ファイル名にスペースが含まれていても安全に扱える。
$ touch 'my file.log' 'another file.log'
$ find . -name '*.log' -exec ls -l {} +
-rw-r--r-- 1 root root 0 Jul 5 13:36 ./another file.log
-rw-r--r-- 1 root root 0 Jul 5 13:36 ./my file.log
findの結果をパイプでxargsにそのまま渡す方法では、スペースを含むファイル名が分割されて壊れる場合がある。
$ find . -name '*.log' | xargs ls -l
ls: cannot access './my': No such file or directory
ls: cannot access 'file.log': No such file or directory
ls: cannot access './another': No such file or directory
ls: cannot access 'file.log': No such file or directory
用途
-exec {} +は以下のような場面で役立つ。
- 検索した大量のファイルに対して、コマンドの起動回数を抑えて処理を高速化したい場合
- 複数ファイルの行数集計など、複数ファイルを1つのコマンドにまとめて渡したい場合
- ファイル名にスペースなど特殊な文字が含まれていても、安全にコマンドへ渡したい場合
更新日時を基準にファイルを検索したい場合は【find -mtime】指定日数より古い(新しい)ファイルを検索する も参照。
ファイルサイズを基準に検索したい場合は【find -size】指定サイズ以上(以下)のファイルを検索する も参照。
