検索結果に対するコマンドの実行(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コマンドを実行」するかが異なり、処理速度が変わってきます。