findで始まるコマンドの終了コードを取得するには?

findで始まるコマンドの終了コードを取得するには?

私はTravis-CIでFindを使ってプログラムで特定のファイル形式をチェックしています。 (正確に言えばシェルチェックです。)

ただし、findそれによって実行されたコマンド/サブシェルの終了コードを使用すると、「メインスクリプト」に渡されず、自然に破棄されます。

たとえば、以下はfindコマンドです。

find . -type f -iname "*.sh" -exec sh ./testScripts.sh "{}" \;

./testScripts.shテスト結果に応じて、0または> = 1で終了することがあります。

正しい終了コードで正しく終了しますが、testScripts.shコマンドfindの終了コードは常に「0」です。私が欲しいのはファイル/実行エラー、このエラーはTravis-CIに「伝播」されました。

どうすればいいですか?

答え1

コメントでStephen Kittの提案を使用してください。

find . -type f -iname "*.sh" -exec sh -c 'for n; do ./testScripts.sh "$n" || exit 1; done' sh {} +

これにより、sh -cスクリプトはゼロ以外の終了状態でできるだけ早く終了しますtestScript.sh。つまり、findゼロ以外の終了状態でも終了します。

プラス記号で終わると、デフォルトのパス名を評価するパス名はセットとして集計され、ユーティリティは同様にセットごとに1回呼び出されますxargs(1)ゼロ以外の終了状態で通話が終了すると、この操作findも最終的に行われます。しかし、これは早期にfind終了しません。


コメントの質問について:

  1. for n; do ... ; done奇妙に見えますが、繰り返すことができる項目がなく、forループが暗黙的に繰り返されることに気付くと理解できます"$@"

  2. sh端の尾は$0殻に配置されますsh -c{}多くのパス名に置き換えられます。そこにない場合、sh最初のパス名は終了し、$0そこにないため、ループから選択されません$@$0通常、現在のインタプリタの名前が含まれます(シェルによって生成されたエラーメッセージに使用されますsh -c)。

答え2

xargsxargsコマンドが失敗すると、1から125(GNUの場合は123)の終了状態で終了し、コマンドが失敗して状態255で終了すると中断されます。

(with)の出力を安定して使用しxargs、コマンドの標準入力を維持するにはGNUが必要です。したがって、GNUとプロセスの交換をサポートするシェルを使用してください。たとえば、次のようになります。find-print0xargsxargskshzshbash

xargs -n1 -r0a <(find . -type f -iname '*.sh' -print0) sh ./testScripts.sh

または、最初の失敗時に中断します。

xargs -r0a <(find . -type f -iname '*.sh' -print0) sh -c '
  for file do
    sh ./testScripts.sh "$file" || exit 255
  done' sh

find(POSIXコード)を使用して、最初のエラーが発生したときに停止することもできます。

find . -type f -name '*.[sS][hH]' -exec sh -c '
  for file do
    if ! sh ./testScripts.sh "$file"; then
      kill -s PIPE "$PPID"
      exit 1
    fi
  done' sh {} +

(SIGPIPEを少なくしてください。騒々しい信号をエクスポートするには、一部のシェルを使用します(例bash:)。これはfind終了し、ゼロ以外の終了状態として返されます。

失敗したコマンド(ここでは最後)の終了ステータスの正確な値を取得するには、またはを使用して次のようにすることzshbashできます。

ret=0
while IFS= read -rd '' -u3 file; do
  sh ./testScripts.sh "$file" 3<&- || ret=$?
done 3< <(find . -type f -iname '*.sh' -print0)

以下を使用しても、これを行うzsh必要はありませんfind

set -o extendedglob
ret=0
for file (./**/*(#i).sh(D.)) {
  ./testScripts.sh $file || ret=$?
}

関連情報