"Argument list too long" の対処 — rm * が失敗するとき

"Argument list too long" の対処 — rm * が失敗するとき

この記事で解決できること

  • rm * / cp * dest/Argument list too long で失敗する原因が分かる
  • find + xargs / find -delete / シェルループで大量ファイルを安全に処理できる
  • スペースや特殊文字を含むファイル名にも対応できる

結論(即解決)

# rm * の代替 — 最も安全
find . -maxdepth 1 -name "*.log" -type f -delete

# または xargs 経由
find . -maxdepth 1 -name "*.log" -print0 | xargs -0 rm

なぜ "Argument list too long" が出るのか?

rm * を実行したとき、ワイルドカードの展開はコマンドではなくシェルが行う。ファイルが数万個あると、展開後の引数リストがカーネルの上限値(ARG_MAX)を超え、カーネルが E2BIG エラーを返す。

$ rm *.log
-bash: /bin/rm: Argument list too long

ARG_MAX の確認:

$ getconf ARG_MAX
2097152
2097152

Linux の一般的な値は 2MB(2,097,152 バイト)。1 ファイルあたりのパス長 × ファイル数がおおむね 200 万文字を超えると発生しやすい。

ls *.logcp *.log dest/cat *.txtchmod 644 *.php など、ワイルドカードを使うすべてのコマンドで同じエラーが発生する

find コマンド自体はファイルを逐次処理するため、この制限を受けない。これが解決策の核心になる。

find -delete で解決する方法

削除だけなら find -delete が最もシンプル。xargs が不要で安全性も高い。

find . -maxdepth 1 -name "*.log" -type f -delete
  • -maxdepth 1:カレントディレクトリのみ(rm * と同等の範囲)
  • -type f:ファイルのみ(ディレクトリの誤削除を防ぐ)
  • -delete:マッチしたファイルを削除

サブディレクトリ以下も対象にする場合は -maxdepth を外す:

find /path/to/dir -name "*.log" -type f -delete

削除前に対象を確認したい場合は -delete を外して実行する。

# dry-run 代わりに件数と一覧を確認
find . -maxdepth 1 -name "*.log" -type f
find . -maxdepth 1 -name "*.log" -type f | wc -l

find + xargs で解決する方法

rm 以外の操作にも使える汎用パターン。

基本パターン

find . -maxdepth 1 -name "*.log" | xargs rm

xargs は自動的に引数を分割してコマンドに渡すため、ARG_MAX を超えない。

ファイル名にスペース・特殊文字が含まれる場合

デフォルトの xargs は空白区切りを使うため、スペースを含むファイル名が誤動作する。-print0xargs -0 を組み合わせると NUL 文字区切りになり安全:

find . -maxdepth 1 -name "*.log" -print0 | xargs -0 rm

実際の運用では -print0 | xargs -0 を常に使うのが無難。

並列処理で高速化する

find . -name "*.log" -print0 | xargs -0 -P4 rm

-P4 で 4 並列実行。大量ファイルの削除が高速化する。

cp / mv でも同じエラーが出る

cp * dest/mv * dest/ も同様に対処できる。

# cp の場合
find . -maxdepth 1 -name "*.log" -print0 | xargs -0 -I{} cp {} /dest/

# mv の場合
find . -maxdepth 1 -name "*.log" -print0 | xargs -0 -I{} mv {} /dest/

-I{} で各ファイルパスを {} に置換する。

-exec を使う方法もある:

find . -maxdepth 1 -name "*.log" -exec cp {} /dest/ \;

-exec はファイルごとにプロセスを起動するため、大量ファイルには xargs のほうが高速。

シェルループで対処する方法

find が使えない場面や、削除前に一件ずつ確認したい場合の代替手段。

for f in *.log; do
    rm "$f"
done

シェルが 1 ファイルずつ rm を呼ぶため ARG_MAX の制限を受けない。

ダブルクォート "$f" を省略すると、スペースを含むファイル名で誤動作する。必ず囲む。

解決策まとめ

方法 コマンド例 向いている場面
find -delete find . -name "*.log" -type f -delete 削除のみ(最もシンプル)
find | xargs rm find . -name "*.log" | xargs rm 標準的な削除
-print0 | xargs -0 find . -name "*.log" -print0 | xargs -0 rm 特殊文字ファイル名
シェルループ for f in *.log; do rm "$f"; done 逐次処理・確認しながら
-exec find . -name "*.log" -exec cp {} /dest/ \; 削除以外の操作

判断フロー

  1. 削除だけ → find -delete
  2. コピー・移動など → find ... -print0 | xargs -0 コマンド
  3. 一件ずつ確認しながら → シェルループ

次に読む