検索結果に対するコマンドの実行(find -exec/xargsコマンド)
検索して見つかった全ファイルを一度に処理したい場合、findコマンドのアクションを利用して次のように指定できます。
$ find . -type f -exec コマンド '{}' \;
「-exec」アクションにより、検索結果に対して「コマンド」を実行します。このとき「{}」がfindコマンドの実行結果、つまりファイルのパスに置き換えられます。「'{}'」のシングルクォートは、シェルの予約語として解釈されるのを避けるために付けています。
ただし上記の例は、ファイル数分「コマンド」が実行されるので時間がかかります。このため、一般的にはxargsコマンドを用いて、次のように指定します(Solarisには対応していません)。
$ find . -type f -print0 | xargs -0 コマンド
xargsコマンドは、標準入力から引数を読み込み、指定の「コマンド」を実行します。上記の例では、findコマンドを利用して見つかった全ファイルのパスを、一度に「コマンド」へ渡しています。
なお、空白や特殊文字を含むファイルを正しく処理するため、findコマンドには必ず「-print0」オプションを付け「xargs -0」で受け取るようにしましょう。「-0」オプションは「--null」でも構いません(FreeBSDでは「-0」オプションのみ利用可能)。
これにより、findコマンドは空白と改行ではなくヌル文字(\0)を区切りとして、検索結果を出力します。同様にxargsコマンドも、引数がヌル文字で区切られているものとして処理を行います。
findコマンドの-execアクションは、シェルを起動しません。シェルコマンドを使いたい場合は、次のように指定します。
$ find … -exec bash -c 'シェルコマンド文字列' \;
xargsコマンドで一度に渡せる長さの制限はOSによって異なります。制限値は「ARG_MAX」というマクロ定数で定義されています。Debian の場合は、次のコマンドで自分の使っているOSのARG_MAXを調べることができます。
$ getconf ARG_MAX 131072
最近のfindコマンド(GNU findの4.2.12以降)では、見つかったファイルを一度に処理するための「-exec コマンド '{}' +」オプションが存在します(FreeBSD、Solarisも対応)。
$ find . -type f -exec コマンド '{}' +
「+」を指定した場合、「{}」が、検索して見つかった全ファイル名のパスに置き換えられます。
xargsコマンドを使わない場合は、シェルの制御構文を使って次のように指定できます。
$ find . -type f -print0 | while read -r -d '' file; do コマンド "$file"; done
ファイル名に「\(バックスラッシュ)」が含まれている場合に備え、readに「-r」オプションを付けています。ただし、この方法も見つかったファイル数分「コマンド」を実行するので、速度は遅くなります。
$ ls
test1.txt test2.txt test3.txt
$ find . -type f -exec ls '{}' \;
./test1.txt
./test2.txt
./test3.txt
$ find . -type f -print0 | xargs -0 ls
./test1.txt ./test2.txt ./test3.txt
$ find . -type f -exec ls '{}' +
./test1.txt ./test2.txt ./test3.txt
$ find . -type f -print0 | while read -r -d '' file; do ls "$file"; done
./test1.txt
./test2.txt
./test3.txt
このようにコマンドの組み合わせによって、「ファイル名のグループを一度にlsコマンドで実行」か「ファイル数分lsコマンドを実行」するかが異なり、処理速度が変わってきます。
