-execが失敗した場合、どうすればfindを失敗させることができますか?


29

このコマンドをシェルで(空でないディレクトリで)実行すると:

find . -exec invalid_command_here {} \;

私はこれを得る:

find: invalid_command_here: No such file or directory
find: invalid_command_here: No such file or directory
find: invalid_command_here: No such file or directory

(各ファイルについてなど)

find最初のエラーの後に失敗する必要があります。これを機能させる方法はありますか?xargsパスにスペースがあるため、を使用できませんが、エラーコードを返すにはこれを呼び出すスクリプトが必要です。

回答:


34

これはの制限ですfindPOSIX標準の戻りステータスがあることを指定しfindたディレクトリを横断中にエラーが発生しない限り、0です。実行されたコマンドの戻りステータスは入力されません。

コマンドにステータスをファイルまたは記述子に書き込ませることができます。

find_status_file=$(mktemp findstatus)
: >"$find_status_file"
find  -exec sh -c 'trap "echo \$?" EXIT; invalid_command "$0"' {} \;
if [ -s "$find_status_file" ]; then
  echo 1>&2 "An error occurred"
fi
rm -f "$find_status_file"

あなたが発見した別の方法は、xargsを使用することです。xargsコマンドは、常にすべてのファイルを処理しますが、コマンドリターンゼロ以外のステータスのいずれかの場合は、ステータス1を返します。

find  -print0 | xargs -0 -n1 invalid_command

さらに別の方法はfind、代わりにシェルで再帰的なグロビングを使用せずに使用**/することです:サブディレクトリの深さを意味します。これには、バージョン4以上のbashが必要です。macOSはバージョン3.xでスタックしているため、ポートコレクションからインストールする必要があります。set -eゼロ以外のステータスを返す最初のコマンドでスクリプトを停止するために使用します。

shopt -s globstar
set -e
for x in **/*.xml; do invalid_command "$x"; done

bash 4.0から4.2では、これは機能しますが、ディレクトリへのシンボリックリンクを通過することに注意してください。これは通常望ましくありません。

bashの代わりにzshを使用する場合、再帰的なグロビングは箱から出してすぐに機能します。ZshはOSX / macOSでデフォルトで利用可能です。zshでは、次のように書くことができます

set -e
for x in **/*.xml; do invalid_command "$x"; done

このxargsアプローチは一般的には機能しbash -cますが、何らかの理由でコマンドが中断します。例:find . -name '*.xml' -print0 | xargs -0 -n 1 -I '{}' bash -c "foo {}"。これは複数回find . -name '2*.xml' -print0 | xargs -0 -n 1 -I '{}' foo {}実行されますが、一度実行されて失敗します。理由は何ですか?
DKroot

@DKrootは絶対に使用しないでください{}内部bash -c。これはファイル名を取得し、シェルコマンド内に直接挿入します。ファイル名にスペースなどのシェル内で特別な意味を持つ文字が含まれている場合、シェルはこれらの特殊文字をそのように解釈します。シェルが必要な場合は{}、別の引数として渡します(例:をbash -c 'foo "$0"' {}囲む二重引用符にも注意してください$0)。
ジル「SO-悪であるのをやめなさい」

質問はさておき、最初のエラーで次が停止しないのはなぜですか?find . -name '*' -print0 | xargs -0 -n 1 -I '{}' bash -c 'foo "$0"' {}
DKroot

@DKrootなぜエラーで停止するのですか?xargsは常にすべてのアイテムでコマンドを実行します。
ジル「SO-悪であるのをやめ

私はこの答えを使用しようとしています:xargs(find . -print0 | xargs -0 -n1 invalid_command)アプローチ。これは最初のエラーで正しく停止します:find . -name '*' -print0 | xargs -0 -n 1 -I '{}' foo {}。すばらしいです!しかし、同じアプローチは上ではうまくいきませんbash -c。2つの唯一の違いはbash -cです。
DKroot


4

xargs1つのオプションです。しかし、それはでこれを行うには、実際に自明簡単ですfind使用することにより、同様+の代わりに、\;

-exec  utility_name  [argument ...]   {} +

POSIXドキュメントから:

一次式がプラス記号で区切られている場合、一次式は常にtrueと評価され、一次式が評価されるパス名は集合に集約されます。ユーティリティutility_nameは、集約されたパス名のセットごとに1回呼び出されます。各呼び出しは、セット内の最後のパス名が集約された後に開始され、findユーティリティーが終了する前に完了し、次のセットの最初のパス名(ある場合)がこのプライマリに対して集約される前に完了しますが、それ以外の場合は呼び出しが指定されません他のプライマリの評価の前、最中、または後に発生します。呼び出しが終了ステータスとしてゼロ以外の値を返す場合、findユーティリティはゼロ以外の終了ステータスを返します。2つの文字「{}」のみを含む引数は、集約されたパス名のセットに置き換えられ、各パス名は、集約されたのと同じ順序で呼び出されたユーティリティに個別の引数として渡されます。2つ以上のパス名のセットのサイズは、ユーティリティの実行によってシステムの{ARG_MAX}制限を超えないように制限されます。2つの文字「{}」のみを含む複数の引数が存在する場合、動作は指定されていません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.