複雑なパイプしたい(xargs)

$ コマンド | xargs コマンド

xargsは、あるコマンドの実行結果を、次のコマンドの引数に変換して渡すコマンドです。

通常のパイプ処理では、前のコマンドの実行結果は、次のコマンドの標準入力として渡されます。 このとき、標準入力に対応していないコマンドだと、処理ができません。 xargsを使って「引数」に変換することで、標準入力に対応していないコマンドでもパイプ処理が可能になります。

ヌル区切りを受け取りたい(--null / -0)

$ コマンド | xargs -0 コマンド
$ コマンド | xargs --null コマンド

--null(-0)で、入力の区切り文字をヌル文字(\0)に変更できます。

xargsはデフォルトで、入力を空白文字(スペース・タブ・改行)で区切って引数に変換します。 そのため、ファイル名に空白文字が含まれていると、1つのファイル名が複数の引数に分割されてしまいます。 ヌル文字(\0 / \0x00 / U+0000)はファイル名に含まれることがない文字コードなので、区切りとして安全です。

findの-print0やfdの-0のように、 出力をヌル文字区切りにできるコマンドと組み合わせて使います。

確認プロンプトしたい(--interactive / -p)

$ コマンド | xargs -p コマンド
$ コマンド | xargs --interactive コマンド

--interactive(-p)で、実行前に確認のプロンプトを挟むことができます。

空実行したくない(--no-run-if-empty / -r)

$ コマンド | xargs -r コマンド
$ コマンド | xargs --no-run-if-empty コマンド

--no-run-if-empty(-r)で、入力が空の場合にコマンドを実行しないようにできます。

参考

Linuxのxargs(GNU版)では、デフォルトで入力が空の場合でもコマンドを実行してしまうので、-rオプションをつける必要があります。

macOS標準のxargs(BSD版)は、デフォルトで入力が空の場合にコマンドを実行しませんが、GNU版との互換性のために、-rオプションが用意されています。

ファイルのパーミッションを一括変更したい

// ファイル: 664に変更
$ find . -type f -print0 | xargs -0 chmod 664

// ディレクトリ: 775に変更
$ find . -type d -print0 | xargs -0 chmod 775

xargsはfind系の検索コマンドとの組み合わせることで力を発揮します。 ここではfindコマンドとchmodコマンドを組み合わせて、ファイルのパーミッションを一括して変更しています。

ファイル名やディレクトリ名に空白文字や特殊文字が含まれている可能性があるため、 find -print0とxargs -0を組み合わせて安全に処理しています。 詳しくは前述の「ヌル区切りを受け取りたい」を参照してください。

// HTMLファイルに限定
$ find . -type f -name "*.html" -print0 | xargs -0 chmod 664

ファイルの種類を限定したい場合は-name "*.拡張子"のようにファイル名の一部を指定します。 ここでは-name "*.html"を指定子、HTMLファイルに限定しています。

// 変更前のパーミッションを記録
$ find . -type f -ls > before.txt

// パーミッションを一括変更
$ find . -type f -print0 | xargs -0 chmod 664

// 変更後のパーミッションを記録して差分を確認
$ find . -type f -ls > after.txt
$ diff before.txt after.txt

変更前後でfind -lsの結果をファイルに保存しておくと、diffでパーミッションがどう変化したかを確認できます。 意図しないファイルまで変更していないか、実行後に見直したい場合に便利です。

// 変更前のパーミッションを記録(macOS/BSD版)
$ find . -type f -print0 | xargs -0 stat -f "%Lp %N" > before.txt

// パーミッションを一括変更
$ find . -type f -print0 | xargs -0 chmod 664

// 変更後のパーミッションを記録して差分を確認
$ find . -type f -print0 | xargs -0 stat -f "%Lp %N" > after.txt
$ diff before.txt after.txt

statを使うと、パーミッションとファイル名だけに絞って記録できます。 find -lsは所有者やサイズなど他の情報も含まれるため差分にノイズが混ざりやすいですが、 statのフォーマット文字列(-fはBSD版、GNU版は-c)で出力項目を絞れば、パーミッションの変化だけを見やすく確認できます。

一括削除したい

// 検索結果にヒットしたファイルをまとめて削除
$ find . -name "*.tmp" | xargs -p rm

--interactive(-p)で、実行前にy/n確認を挟むことができます。 xargs -p rmとすると、削除する前に確認のプロンプトを表示できます。

// 検索結果をファイルに保存してから削除
$ fd -0 "検索パターン" > files.txt
$ cat files.txt
$ xargs -0 rm < files.txt
$ mv files.txt ${date +%Y%m%d}_removed_files.txt

削除の記録を残したい場合、対象となるファイルを一度ファイルに保存してから削除する方法もあります。

注釈

同様の実行結果は rm "検索パターン" でも得られます。 しかし、どのファイルが削除されるのか、確認が難しいです。

xargsの実用的なポイントとして fd "検索パターン"の結果を一度ファイルに保存し、 cat files.txtで中身を確認、 問題がなければxargs -0 rm < files.txtで削除、 のように確認→実行のフローが作れることです。