findコマンドとxargsの組み合わせ術:ファイル検索から一括処理(exec)までの実務レシピ

実務レシピ

サーバー運用やログ解析において、「特定の条件に合致するファイルを探し出し、それらに対して一括で処理を行う」という作業は日常茶飯事です。この用途で最強の組み合わせとなるのが、find コマンドと -exec オプション、そして xargs コマンドです。

本記事では、単なる検索オプションの解説にとどまらず、実務でそのまま使える「検索して一括処理する」ためのレシピを厳選して紹介します。

1. find コマンドの基本と実務で使うオプション

find コマンドの基本的な構文は以下の通りです。

find [検索パス] [検索条件] [アクション]

実務で頻繁に使用する検索条件(オプション)は限られています。以下の表にまとめました。

オプション意味実務での使用例
-nameファイル名で検索(ワイルドカード可)-name "*.log"
-typeファイルタイプで検索(f:ファイル, d:ディレクトリ)-type f
-mtime更新日時で検索(+N:N日より前, -N:N日以内)-mtime +30(30日以上前のファイル)
-sizeファイルサイズで検索(+N:Nより大きい)-size +100M(100MB以上のファイル)

注意点: -name でワイルドカード(*)を使用する場合は、必ずクォーテーション(" " または ' ')で囲んでください。囲まないと、シェルによってカレントディレクトリのファイル名に展開されてしまい、意図しない動作を引き起こします。

2. 検索結果に対してコマンドを実行する(-exec)

見つけたファイルに対して、直接コマンドを実行できるのが -exec アクションです。

find /var/log/app -name "*.log" -mtime +30 -exec rm {} \;

このコマンドは、「/var/log/app 配下の .log ファイルのうち、更新日時が30日より前のものを削除(rm)する」という処理を行います。

-exec の構文ルール

  • {} : find で見つかったファイルパスに置き換わります。
  • \; : -exec で実行するコマンドの終わりを示します(セミコロンをシェルからエスケープするためにバックスラッシュが必要です)。

+ を使った効率的な実行

\; の代わりに + を使用すると、見つかった複数のファイルをまとめて1つのコマンドの引数として渡すことができます。

# 1ファイルずつ rm を実行(ファイル数だけプロセスが起動する)
find . -name "*.tmp" -exec rm {} \;

# まとめて rm を実行(効率的)
find . -name "*.tmp" -exec rm {} +

3. xargs を使ったより高度な一括処理

-exec よりも柔軟で、パイプ(|)と組み合わせて強力な処理を実現できるのが xargs コマンドです。xargs は標準入力から受け取ったリストを、指定したコマンドの引数として渡します。

find と xargs を組み合わせる際の鉄則(-print0 と -0)

ファイル名にスペースが含まれている場合、通常の xargs ではスペースで区切られた別々のファイルとして認識されてしまい、エラーになります。これを防ぐために、find 側で -print0(ヌル文字区切りで出力)、xargs 側で -0(ヌル文字区切りで受け取る)を使用するのが鉄則です。

find /path/to/search -type f -name "*.txt" -print0 | xargs -0 grep "ERROR"

-I オプション:引数の位置を任意に指定する

xargs はデフォルトでコマンドの末尾に引数を追加しますが、-I オプションを使うと置換文字列(プレースホルダー)を指定して、コマンド中の任意の位置に引数を挿入できます。

# -I {} で {} を置換文字列として指定
find /backup -name "*.sql" -print0 | xargs -0 -I {} cp {} /archive/

# 拡張子を付け替える例(元のファイル名を2箇所で使用)
ls *.txt | xargs -I FILE mv FILE FILE.bak

-I を指定すると1入力につき1回コマンドが実行されます。複数の引数をまとめて渡したい場合は代わりに -n オプションを使用してください。

-P オプション:並列実行でスループットを上げる

-P--max-procs)オプションを使うと、xargs が同時に起動するプロセス数を制御できます。CPUコアを活かして大量ファイルの処理を高速化したいケースに有効です。

# -P 4 で最大4プロセスを並列実行
find /images -name "*.png" -print0 | xargs -0 -P 4 -I {} convert {} -quality 80 {}.jpg

# grep を並列化してログ解析を高速化
find /var/log -name "*.log" -print0 | xargs -0 -P 8 grep "ERROR"

-P 0 を指定すると可能な限り多くのプロセスを同時起動しますが、CPUやメモリを使い切る恐れがあるため、本番環境では適切な数値(コア数程度)を指定するのが安全です。

4. 実務でそのまま使えるレシピ集

レシピ1:古いログファイルを一括で圧縮(gzip)する

ログローテーションが設定されていない環境などで、手動で古いログを圧縮したい場合に使用します。

# 7日以上前の .log ファイルを gzip で圧縮
find /var/log/myapp -type f -name "*.log" -mtime +7 -print0 | xargs -0 gzip

レシピ2:特定の文字列を含むファイルを検索し、該当箇所を表示する

プロジェクト内の全ソースコードから、特定の関数名やエラーメッセージを探し出す際によく使います。

# .php ファイルの中から "TODO" という文字列を探す
find /var/www/html -type f -name "*.php" -print0 | xargs -0 grep -n "TODO"

レシピ3:空のディレクトリを一括で削除する

不要になった一時ディレクトリなどを整理する際に便利です。

# 空のディレクトリ(-empty)を検索して削除
find /tmp/cache -type d -empty -exec rmdir {} +

レシピ4:パーミッションが不適切なファイルを一括修正する

Webサーバーの公開ディレクトリなどで、ファイルやディレクトリの権限を正しく設定し直すための定番コマンドです。chmodコマンドの数値(755・644など)の意味については「Linuxのchmodコマンドとは?権限変更・755/644の意味・実行権限を解説」を参照してください。

# ディレクトリの権限を 755 に設定
find /var/www/html -type d -exec chmod 755 {} +

# ファイルの権限を 644 に設定
find /var/www/html -type f -exec chmod 644 {} +

レシピ5:grep + xargs でパターンを含むファイルを一括処理する

grep -rl(ファイル名のみ再帰的に出力)と xargs を組み合わせると、「特定パターンに一致したファイルだけを後続コマンドに渡す」というフィルタリングパイプラインを構築できます。grep の詳しい使い方と合わせて活用してください。

# "ERROR" を含む .log ファイルを /archive に移動
grep -rl "ERROR" /var/log --include="*.log" | xargs -I {} mv {} /archive/

# 古い関数名が残っているファイルを一括置換(sed と組み合わせ)
grep -rl "old_function" /var/www/html | xargs sed -i 's/old_function/new_function/g'

# 特定設定値が残っているファイルと行番号を一覧表示
grep -rl "deprecated_config" /etc | xargs grep -n "deprecated_config"

grep -l はマッチしたファイル名のみを出力するため、xargs との相性が抜群です。スペースを含むパスが混在する場合は grep --nullxargs -0 を組み合わせてください。

レシピ6:-P オプションで並列処理しファイル変換を高速化する

大量のファイルに変換処理を行う場合、-P オプションで並列実行することで処理時間を大幅に短縮できます。

# 画像を4プロセス並列でリサイズ
find /images -name "*.jpg" -print0 | xargs -0 -P 4 -I {} convert {} -resize 800x600 /resized/{}

# JSONファイルを8プロセス並列でバリデーション
find /data -name "*.json" -print0 | xargs -0 -P 8 python3 -c "import sys,json; json.load(open(sys.argv[1]))" {}

# テキストファイルを並列圧縮(コア数に合わせて調整)
find /logs -name "*.log" -mtime +7 -print0 | xargs -0 -P $(nproc) gzip

5. 破壊的操作を行う前の安全確認

rmchmod などの破壊的操作を伴うコマンドを実行する際は、必ず事前にアクション(-exec や xargs)を外した状態で実行し、対象となるファイルのリストを目視確認してください。

# 1. まずはリストだけを出力して確認
find /var/log -name "*.log" -mtime +30

# 2. 問題なければ -exec や xargs を付与して実行
find /var/log -name "*.log" -mtime +30 -exec rm {} +

まとめ

find コマンドは単なる「検索ツール」ではなく、-execxargs と組み合わせることで、強力な「一括処理ツール」へと進化します。本記事で紹介した主要ポイントをまとめます。

  • -print0 / -0:スペースを含むファイル名に対応するためのベストプラクティス
  • -I オプション:置換文字列でコマンド内の任意位置に引数を挿入
  • -P オプション:プロセス数を指定した並列実行で大量処理を高速化
  • grep + xargs:マッチしたファイル名を後続コマンドに渡すフィルタリングパイプライン

各コマンドの詳細は関連記事も参照してください。find – ファイルやディレクトリを検索するコマンドでは検索条件の網羅的な解説を、grep – テキスト検索を行うコマンドでは正規表現や各種オプションの使い方を確認できます。

ディレクトリ構造を視覚的に確認したい場合は、Linuxのtreeコマンドも合わせて活用すると便利です。findがファイル検索・処理に特化しているのに対し、treeは階層構造の可視化に優れています。

Bash玄

はじめまして!Bash玄です。

エンジニアとしてシステム運用に携わる中で、手作業の多さに限界を感じ、Bashスクリプトを活用して業務を効率化したのがきっかけで、この道に入りました。「手作業は負け」「スクリプトはシンプルに」をモットーに、誰でも実践できるBashスクリプトの書き方を発信しています。

このサイトでは、Bashの基礎から実践的なスクリプト作成まで、初心者でもわかりやすく解説しています。少しでも「Bashって便利だな」と思ってもらえたら嬉しいです!

# 好きなこと
- シンプルなコードを書くこと
- コマンドラインを快適にカスタマイズすること
- 自動化で時間を生み出すこと

# このサイトを読んでほしい人
- Bashに興味があるけど、何から始めればいいかわからない人
- 定型業務を自動化したい人
- 効率よくターミナルを使いこなしたい人

Bashの世界に一歩踏み出して、一緒に「Bash道」を極めていきましょう!

Bash玄をフォローする