探す。 - findで0 | xargs -0 cmdを印刷します。 -exec コマンド {}+

探す。 - findで0 | xargs -0 cmdを印刷します。 -exec コマンド {}+
  • 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これを確実に行うことはできません。

-01990年にGNUにオプションが追加され、xargs新しい-print0オプションが追加されました。 findGNUの作者がオプションを追加したときにSysVについて知らなかったことは確かですfind。それ以来、NULで区切られた問題を処理するために、-exec {} +いくつかの -0///--nullオプションが他のGNUユーティリティに徐々に追加されました。-z--zero交換すべてのファイルパスとより一般的には、すべてのC文字列またはコマンドライン引数の形式を使用できます。

-dxargsシングルバイトレコード区切り文字を許可するオプションは、-0後でGNU xargs(4.2.26で2005年後半にリリース)に追加されたものと同じであるため、冗長ですが、-d '\0'これまで知っている限り、まだGNUでサポートされていますxargs

-0これら-d(または-r以下を参照)がないとxargs(安定して)使用することはほとんど不可能です。

-print0/は以来、いくつかの異なる実装、さらにはSolaris 11などのいくつかの商用SysV派生Unixにも追加されました。シェルの組み込みコマンド-0もこれをサポートします。findbosh

標準語ではありませんが、-d ''これは、POSIX規格の次のバージョン(ユーティリティオプション付き)でも可能です。read

-exec cmd {} + xargs 以上 -0 cmd

-exec cmd {} +標準であり、現在は移植性に優れています。ビジーボックスでは、サポートはまだオプションであるため、利用できないLinuxベースの組み込みシステムがある可能性があります。-ok cmd {} +実行前にユーザーにメッセージを表示するバリアントはcmd標準でも移植性もありません(コマンドラインがかなり大きくなる可能性があるため便利ではありません)。

-print0/は標準ではありませんが、BSDおよびLinuxベースのシステム(GNU、busybox 'およびtoybox'を含む)の/実装xargs -0では一般的です。まだサポートされていませんfindxargsAIXではHP/UXも同様です。

GNUシステムの外部では、NULで区切られたレコードをサポートする他の標準ユーティリティ(sortなどsed)の他の実装を見つけることはまだまれです。cutawk

find-print0標準実装で使用できますが、/ / ... -zと同等の標準は-exec printf '%s\0' {} +なく、より一般的にはNULはPOSIXテキストユーティリティ(または一般的なファイルパスでも処理できません。テキスト)。xargs -0sortsedgrep

一部のBSDを除いて、通常不要なファイルが見つからない場合は、引数なしで一度だけfind . -print0 | xargs -0 cmd実行されます。cmdGNU実装はこれを防ぐためのオプションをxargs追加しますが。-r-0

ではstdinfind . -exec cmd {} +cmd継承されるため、たとえば端末でコマンドが実行された場合findでもcmd、ユーザーと対話できます。

の実装find . -print0 | xargs -r0 cmdによっては、stdinは/ dev / null(GNUと同じ)であるか、さらに悪いことにstdinが出てくるパイプである 'stdinを継承するので、stdinから読むと重大な損傷を引き起こす可能性があります。 GNU実装を使用すると、代替を使用して処理することでこの問題を解決できます。xargscmdxargsxargsfindxargs-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:)を使用して呼び出すことができます。/varfind/var

この問題は影響を受けません-exec cmd {} +

を使用するとfind . -exec cmd {} +終了ステータスが反映され、find失敗cmdしますが、find | xargsほとんどのシェルは終了ステータスのみを取得するxargsため、すべてのファイルが見つからないという事実を見逃す可能性があります。多くのシェルにはpipefailこれを軽減するコマンドがあります。最終的により柔軟性を提供するzsh$pipestatusまたはbashも参照してください$PIPESTATUS

(非標準)-execdir cmd -- {} +(ファイル名にプレフィックスを追加しない実装に必要な注--)バリアントを解決できます。find./-exec cmd {} +xargsxargs

パフォーマンスの観点からはfind | xargs、これはより多くの作業(少なくとも1つの追加のプロセスとパイプを介して対応するデータプッシュ)を意味しfindますxargs。 2 つが I/O アクセスを配置して競合findしないため、cmdより多くのリソース全体を使用できるため、競合が発生します。このような並列性により、場合によっては、以前のバッチで CPU を大量に使用する一部のジョブを実行している間 (少なくともパイプラインおよび内部出力が実行されるまで)、より多くのファイルを検索し続けることがfindできるため、ジョブの実行速度が速くなる可能性があります. 。 )バッファがいっぱいです。)cmdfind

を使用すると、検索全体を中断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 255xargsfind

-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-scmdxargs-Pcmdxargs -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言語環境で動作しますfindcmdそして

LC_ALL=C find . -exec env -u LC_ALL cmd {} +

最初のものは非標準ですが、事前定義するcmdLC_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含まれている;と失敗します。{}+

関連情報