find . -exec cmd {} +
find . -print0 | xargs -0 cmd
どちらも find で見つかったファイルに対してコマンドを実行する安定した方法です。
どちらが好まれますか?どちらがより持ち運び可能で、信頼性が高く、効率的で汎用性が高く、その理由は何ですか?
答え1
長い話を短く
確かな勝者はいません。私の提案は以下を使用することです。
find . -exec cmd {} +
移植性に優れ、リソースの使用量が少なく、問題が少なく、次のいずれかを満たすことで十分です。
xargs -r0 -other-options -a <(find ... -print0 | ...) cmd
find . -print0 | ... | xargs -0 -other-options cmd
他のツールの追加機能が必要な場合、またはxargs
他のツールの出力を後処理する必要があり、find
現在使用されているシステムがこれらの非標準オプションをサポートし、これらの制限が適用されないか無視される可能性があることを知っている場合。
歴史
find
すでに-exec cmd {} ';'
この亜種は
cmd
ファイルごとに単一の呼び出しを実行し、条件付き述部としても機能します。 70年代半ば、Unix V5では現在のインタフェースとして再実装されましたが、この-exec cmd {} +
形式は複数のファイルをに渡しますcmd
。 。 David Kornが作成し、1988年にSystem Vリリース4で最初にリリースされましたが(参考資料を参照lynx news://news.gmane.io/gmane.comp.standards.posix.austin.general/2192
)、SVR4.2(1992)までは文書化されていません。
これだけPOSIX標準2001バージョンに追加されましたいくつかの実装ではfind
後で追加しました(4.2.12、GNU 2005
find
、FreeBSD 2002、NetBSD 2006、busybox 2015)。
xargs
それ自体は1970年代後半のPWB Unixに由来していました。奇妙で不要な機能と制限がある非常に過酷なインターフェースを持っていて(まだ持っています)、ユニークな形式の引用を理解しています(まだ生き残っていないPWB Unixシェルが理解しているものにかなり近いですが)。その目的は出力を処理することですが、find
これを確実に行うことはできません。
-0
1990年にGNUにオプションが追加され、xargs
新しい-print0
オプションが追加されました。
find
GNUの作者がオプションを追加したときにSysVについて知らなかったことは確かですfind
。それ以来、NULで区切られた問題を処理するために、-exec {} +
いくつかの
-0
///--null
オプションが他のGNUユーティリティに徐々に追加されました。-z
--zero
交換すべてのファイルパスとより一般的には、すべてのC文字列またはコマンドライン引数の形式を使用できます。
-d
xargs
シングルバイトレコード区切り文字を許可するオプションは、-0
後でGNU xargs(4.2.26で2005年後半にリリース)に追加されたものと同じであるため、冗長ですが、-d '\0'
これまで知っている限り、まだGNUでサポートされていますxargs
。
-0
これら-d
(または-r
以下を参照)がないとxargs
(安定して)使用することはほとんど不可能です。
-print0
/は以来、いくつかの異なる実装、さらにはSolaris 11などのいくつかの商用SysV派生Unixにも追加されました。シェルの組み込みコマンド-0
もこれをサポートします。find
bosh
標準語ではありませんが、-d ''
これは、POSIX規格の次のバージョン(ユーティリティオプション付き)でも可能です。read
。
-exec cmd {} + xargs 以上 -0 cmd
-exec cmd {} +
標準であり、現在は移植性に優れています。ビジーボックスでは、サポートはまだオプションであるため、利用できないLinuxベースの組み込みシステムがある可能性があります。-ok cmd {} +
実行前にユーザーにメッセージを表示するバリアントはcmd
標準でも移植性もありません(コマンドラインがかなり大きくなる可能性があるため便利ではありません)。
-print0
/は標準ではありませんが、BSDおよびLinuxベースのシステム(GNU、busybox 'およびtoybox'を含む)の/実装xargs -0
では一般的です。まだサポートされていませんfind
xargs
AIXではHP/UXも同様です。
GNUシステムの外部では、NULで区切られたレコードをサポートする他の標準ユーティリティ(sort
などsed
)の他の実装を見つけることはまだまれです。cut
awk
find
-print0
標準実装で使用できますが、/ / ... -zと同等の標準は-exec printf '%s\0' {} +
なく、より一般的にはNULはPOSIXテキストユーティリティ(または一般的なファイルパスでも処理できません。テキスト)。xargs -0
sort
sed
grep
一部のBSDを除いて、通常不要なファイルが見つからない場合は、引数なしで一度だけfind . -print0 | xargs -0 cmd
実行されます。cmd
GNU実装はこれを防ぐためのオプションをxargs
追加しますが。-r
-0
ではstdinfind . -exec cmd {} +
がcmd
継承されるため、たとえば端末でコマンドが実行された場合find
でもcmd
、ユーザーと対話できます。
の実装find . -print0 | xargs -r0 cmd
によっては、stdinは/ dev / null(GNUと同じ)であるか、さらに悪いことにstdinが出てくるパイプである 'stdinを継承するので、stdinから読むと重大な損傷を引き起こす可能性があります。 GNU実装を使用すると、代替を使用して処理することでこの問題を解決できます。xargs
cmd
xargs
xargs
find
xargs
-a
xargs -r0a <(find . -print0) cmd # Korn syntax
xargs -r0a <{find . -print0} cmd # rc syntax
xargs -r0a /dev/fd/3 3<(find . -print0) cmd # yash syntax
xargs -r0a (find . -print0|psub) cmd # fish syntax (not parallel though)
しかし、携帯性ははるかに低下します。
安定性の観点から競合が発生したfind . -print0 | xargs -0 cmd
場合、または早期に終了した場合(リソースの制限に達した場合など)、find
重大な結果が生じる可能性があります。これはfind
、NUL区切り文字で終わることが保証されていないブロックに出力を作成し(例、with find /var/tmp -name '*.tmp'
、ブロックはで終わることがあります)、/var
区切られていないレコードのxargs
引数を提供し続けるためです。cmd
たとえば、私たちの例では、.で終わるブロックを出力して死亡した場合、as引数cmd
(例rm -rf
:)を使用して呼び出すことができます。/var
find
/var
この問題は影響を受けません-exec cmd {} +
。
を使用するとfind . -exec cmd {} +
終了ステータスが反映され、find
失敗cmd
しますが、find | xargs
ほとんどのシェルは終了ステータスのみを取得するxargs
ため、すべてのファイルが見つからないという事実を見逃す可能性があります。多くのシェルにはpipefail
これを軽減するコマンドがあります。最終的により柔軟性を提供するzsh$pipestatus
またはbashも参照してください$PIPESTATUS
。
(非標準)-execdir cmd -- {} +
(ファイル名にプレフィックスを追加しない実装に必要な注--
)バリアントを解決できます。find
./
-exec cmd {} +
xargs
xargs
パフォーマンスの観点からはfind | xargs
、これはより多くの作業(少なくとも1つの追加のプロセスとパイプを介して対応するデータプッシュ)を意味しfind
ますxargs
。 2 つが I/O アクセスを配置して競合find
しないため、cmd
より多くのリソース全体を使用できるため、競合が発生します。このような並列性により、場合によっては、以前のバッチで CPU を大量に使用する一部のジョブを実行している間 (少なくともパイプラインおよび内部出力が実行されるまで)、より多くのファイルを検索し続けることがfind
できるため、ジョブの実行速度が速くなる可能性があります. 。 )バッファがいっぱいです。)cmd
find
を使用すると、検索全体を中断find . -exec cmd {} +
する方が簡単です。cmd
たとえば、次のようになります。
find . -exec sh -c 'if some-condition; then kill -s PIPE "$PPID"; exit 1; fi' sh {} +
使用するとfind . -print0 | xargs -0 cmd
中断がcmd
発生する可能性がありますが、パイプに次のブロックを書き込もうとするまで終了しません。exit 255
xargs
find
-exec cmd {}のxargs -0 cmd +
この見解の主な主張は、それが一般的な意味でより一般的で、より柔軟で、より汎用性があるということです。
find
の出力は、-print0
でのみ使用できるファイルリストの後処理可能な表現ですxargs -0
。たとえば、次のようにできます。
find . -print0 |
grep -z foo |
sort -z
また、後処理、フィルタリング、並べ替えが可能なファイルパスのリストを取得できます。
ファイルパスを表すかどうかを示すか、出力であるか、別のものをNULで区切られたリストにも同じようにxargs -0
使用できます。find
これに関連して、find . -exec cmd {} +
これは狭い特別なユースケースにのみ適しています(最も一般的なユースケースの1つにもかかわらず)。
またはオプションを使用して、に渡される引数の数を制限xargs
できます。 GNUの場合は、複数のインスタンスを並列に実行するオプション、またはファイルのリストの後に追加の引数を追加できるいくつかのBSDオプションも参照してください。-n
-s
cmd
xargs
-P
cmd
xargs -0 -J {} mv {} /dest/
出力をfind . -print0
ファイルに保存して後で処理することで(たとえば、find
成功した場合のみ)、次のような出力が結果をxargs -0 cmd < file
妨げcmd
ないようにすることができますfind
(GNUを使用xargs
)。
xargs -r0a =(find . -print0) cmd # zsh
xargs -r0a (find . -print0|psub -f) cmd # fish
特殊処理と同等のものはありませんxargs
(上記内容参照)。exit 255
-exec cmd {} +
kill "$PPID"
を使用すると、find | xargs
他のロケールまたはより一般的に異なる環境(変数、制限、umaskなどを含む)でより簡単にfind
実行できます。xargs cmd
たとえば、テキストではなくファイル名に関する問題を解決するためにCロケールで実行する必要がある場合がありますが、それでもfind
ユーザーcmd
のロケールで実行したい場合があります。
LC_ALL=C find . -exec cmd {} +
C言語環境で動作しますfind
。cmd
そして
LC_ALL=C find . -exec env -u LC_ALL cmd {} +
最初のものは非標準ですが、事前定義するcmd
とLC_ALL
元のロケールを復元することは不可能かもしれません。
LC_ALL=C find . -print0 | xargs -r0 cmd
find
ロケールをCに変更するだけです。
特別な場合:
find . -exec sudo cmd {} +
sudo
通常、環境変数の設定によって引数リストがSUDO_COMMAND
複製されるため、args + envサイズの制限を回避する方法はありません。
find . -print0 | sudo xargs -r0 cmd
$SUDO_COMMAND
この場合はインクルードのみをxargs -0 cmd
使用するため、問題はありませんfind . -print0 | xargs -r0 sudo cmd
。
また見なさい:
sudo find . -print0 | xargs -r0 cmd
ファイルのリストはによって検出されますが、root
元のcmd
ユーザーとして実行されます。または
find . -print0 | (USERNAME=some-user; xargs -r0 cmd)
zshのようなシェルは(e)uid、(e)gidの変更をデフォルトでサポートします。
find . -print0 | xargs -r0 -- "${cmd[@]}"
$cmd
配列に含まれる内容に関係なく機能しますが、
find . -exec "${cmd[@]}" {} +
配列の要素の 1 つでも、連続した要素があるか$cmd
含まれている;
と失敗します。{}
+