"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 *.log、cp *.log dest/、cat *.txt、chmod 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 は空白区切りを使うため、スペースを含むファイル名が誤動作する。-print0 と xargs -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/ \; |
削除以外の操作 |
判断フロー
- 削除だけ →
find -delete - コピー・移動など →
find ... -print0 | xargs -0 コマンド - 一件ずつ確認しながら → シェルループ