web-dev-qa-db-ja.com

findによって開始されたコマンドの終了コードを取得するにはどうすればよいですか?

Travis-CIで「検索」を使用して、特定のファイルタイプをプログラムでチェックしています。 (正確には、これはシェルチェックチェックです。)

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

例として、これは検索コマンドです。

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

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

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

どうすればこれを達成できますか?

4
rugk

コメントでスティーブン・キットの提案を使用する:

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

これにより、_sh -c_が終了するとすぐに、_testScript.sh_スクリプトがゼロ以外の終了ステータスで終了します。つまり、findもゼロ以外の終了ステータスで終了します。

プラス記号で終了すると、プライマリが評価されるパス名がセットに集約され、xargs(1)と同様に、ユーティリティはセットごとに1回呼び出されます。 ゼロ以外の終了ステータスで呼び出しが終了した場合、findは最終的には同様に終了しますですが、これによってfindが早く終了することはありません。


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

  1. _for n; do ... ; done_は奇妙に見えますが、何も繰り返す必要がないと、forループが暗黙的に_"$@"_を反復することに気づくと意味があります。

  2. 末尾のshは、_$0_シェルの_sh -c_に配置されます。 _{}_は、いくつかのパス名に置き換えられます。そこにshがないと、最初のパス名は_$0_になってしまい、_$@_にないため、ループによって取得されません。 _$0_には通常、現在のインタープリターの名前が含まれます(_sh -c_シェルによって生成されるエラーメッセージで使用されます)。

5
Kusalananda

xargsは、いずれかのコマンドが失敗した場合、1〜125の終了ステータス(GNU xargsの123)で終了し、失敗した場合は中止されます。ステータスは255です。

xargsの出力でfindを確実に使用するには(-print0)コマンドの標準入力を保持するには、GNU xargsが必要です。したがって、GNU xargskshzshまたはbashなどのプロセス置換をサポートするシェル:

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

(POSIXコード)の最初のエラー時にfindを中止することもできます。

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 {} +

bashのようないくつかのシェルでSIGPIPEをless noisy信号として使用)。これによりfindが強制終了され、ゼロ以外の終了ステータスで戻ります。

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

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=$?
}
1