大量のファイルを一気に消したい時はxargsを使う

先日、テスト実行で生成された大量のログファイルを消そうとしてうまくいかず、少し時間を使ってしまったので書いておく。仮に log0000.json から log9999.json まで10,000ファイルあるとする。
これらを一気に消そうと思い普通にrmを使ったところ、実行に失敗した。

$ rm testsort_*.json
argument list too long: rm

これはエラーの通り、アスタリスクを展開したときのコマンドが長くなりすぎて、システムで処理できないということ。どうすればいいか悩んだが、ファイルを1つずつ削除すれば良い話。
例えばxargsを使えば以下のようにできる。

$ find . -name "log*.json" | xargs rm

(2020/11/16: 以下部分の誤った記述を修正)
xargsは、標準入力として受け取ったデータを1つずつ逐次実行していく。そのため、実行中にディレクトリを覗くとファイルが除々に減っていくのがわかる。forループを使っても良いが、その場合 find コマンドの実行が終了してからでないと削除に移らない。時間がかかる処理の場合正しく進行しているのかわかりにくいため、個人的にはxargsの方が好み。

上の話は xargs -I の場合。デフォルトのxargsは、受け取った全ての引数をまとめてコマンドに渡す。ただし、コマンドが取れる引数の数の限界になるまで引数をセットしたらコマンドを1回実行する。余った引数は、2回目、3回目のコマンドに渡されて実行される。
このため、今回のように大量のファイルを消す場合、可能な限り最大数の引数を取ってrmが何度か実行される。実行中にディレクトリを覗くとファイルが除々に減っていくのがわかる。xargsを使うことで、rmの実行回数が最小限に抑えられ、パフォーマンス的にも良い。
以下、xargsのmanより。

The command line for command is built up until it reaches a system-defined limit (unless the -n and -L options are used). The specified command will be invoked as many times as necessary to use up the list of input items. In general, there will be many fewer invocations of command than there were items in the input. This will normally have significant performance benefits.