find -exec と xargs の使い分け
結論:使い分けの基準
基本方針:シンプルな処理は -exec {} +、パイプライン組み込みや並列処理が必要なら xargs。
| 状況 | 推奨 |
|---|---|
| シンプルな1コマンド実行 | find -exec {} + |
| スペースを含むファイル名 | find -print0 | xargs -0 |
| 並列実行が必要 | xargs -P |
| パイプラインに組み込む | xargs |
| シェル機能(変数・条件分岐)が必要 | find -exec sh -c '...' _ {} \; |
スペース対策の鉄則
ファイル名にスペースが含まれる可能性がある環境では、常に -print0 | xargs -0 の組み合わせを使う。
find -exec の仕組み
find -exec は find の組み込み機能で、検索結果に対して直接コマンドを実行する。外部ツールへのパイプが不要なため、シンプルな処理に向いている。
セミコロン形式(\;):1件ずつ実行
find /var/log -name "*.log" -exec ls -lh {} \;{} が見つかったファイルパスに置換される。\; はシェルのセミコロン解釈を防ぐエスケープ。1ファイルごとにコマンドが起動するため、ファイル数が多いと低速になる。
プラス形式(+):まとめて実行
find /var/log -name "*.log" -exec ls -lh {} ++ を末尾に使うと find が引数をバッファリングし、可能な限りまとめてコマンドを1回呼び出す。処理速度が \; より大幅に向上する。
-exec {} + は POSIX 準拠で外部ツール不要。ファイル名にスペースが含まれていても正しく処理できる。
xargs の仕組み
xargs は標準入力のデータをコマンドの引数に変換して実行する。パイプラインとの連携が得意。
find /var/log -name "*.log" | xargs ls -lh
find の出力を改行区切りで読み込み、ls -lh file1 file2 file3 ... のようにまとめて実行する。
主なオプション:
-0:NUL文字区切りで読み込む(-print0と組み合わせる)-I {}:プレースホルダで引数位置を指定する-P N:N プロセスで並列実行する-r:入力が空のときコマンドを実行しない
スペース・特殊文字を含むファイル名への対処
ファイル名にスペースや改行が含まれると、デフォルトの xargs は誤動作する。
# 危険:スペース入りファイル名で誤分割が起きる find . -name "*.txt" | xargs rm # 安全:NUL文字区切りを使う find . -name "*.txt" -print0 | xargs -0 rm
-print0 は find の出力区切りをNUL文字(\0)にする。xargs -0 はNUL文字で区切って読む。この組み合わせであらゆる特殊文字を含むファイル名を安全に処理できる。
find -exec {} + を使う場合はパイプを通さないため、スペースの問題は最初から発生しない。
本番環境ではファイル名にスペースが含まれる可能性を常に考慮すること。
並列実行:xargs -P
大量ファイルの処理速度が問題になる場合、xargs -P でプロセスを並列化できる。
# 4並列で画像変換
find . -name "*.png" -print0 | xargs -0 -P 4 -I {} convert {} {}.jpg-P 4 で最大4プロセスを同時起動する。CPU コア数に近い値が目安。
find -exec にはネイティブの並列実行機能がない。並列処理が必要なら xargs -P を選ぶ。
シェル機能が必要なとき
find -exec も xargs も直接シェル構文(変数・パイプ・リダイレクト)は実行できない。その場合は sh -c を経由する。
# find -exec でシェル機能を使う(推奨形)
find . -name "*.log" -exec sh -c 'wc -l "$1" >> /tmp/result.txt' _ {} \;
# xargs でシェル機能を使う
find . -name "*.log" -print0 | xargs -0 -I {} sh -c 'wc -l "{}" >> /tmp/result.txt'sh -c 内で {} を直接文字列展開するとシェルインジェクションのリスクがある。find -exec sh -c '...' _ {} \; の形式で $1 を使う方が安全。
よく使うパターン集
古いログファイルの削除
# 30日以上前の .log を削除
find /var/log -name "*.log" -mtime +30 -exec rm -v {} +ファイル・ディレクトリの一括パーミッション変更
# ディレクトリのみ 755
find . -type d -exec chmod 755 {} +
# ファイルのみ 644
find . -type f -exec chmod 644 {} +テキスト一括置換
# grep で対象を絞り、sed で置換 find . -name "*.conf" -print0 | xargs -0 grep -l "old_value" | xargs sed -i 's/old_value/new_value/g'
空ディレクトリの削除
find . -type d -empty -exec rmdir {} +まとめ
| 比較軸 | find -exec {} + | xargs |
|---|---|---|
| 外部ツール依存 | なし | xargs が必要 |
| スペース対応 | デフォルトで安全 | -print0 | xargs -0 が必要 |
| 並列実行 | 不可 | -P で可能 |
| パイプライン組み込み | 難しい | 容易 |
| 構文の簡潔さ | シンプル | パイプが必要 |
スペースを含まないシンプルな処理なら -exec {} + が最もシンプル。パイプラインへの組み込み・並列処理・xargs -0 が必要なケースでは xargs を選ぶ。