条件付きエラーが発生した場合、「set -e」はスクリプトを終了しません。

条件付きエラーが発生した場合、「set -e」はスクリプトを終了しません。

次のスクリプトには構文エラーまたはいくつかのエラーがあります。

#!/usr/bin/env bash
set -euo pipefail

if [ ! -f /custom.log]; then
  echo "test"
fi
abcxyz

スクリプトが出力で失敗します。

./test.sh: line 4: [: missing `]'
./test.sh: line 7: abcxyz: command not found

このスクリプトを修正する方法には興味がありませんが、このエラーが発生した場合にスクリプトが継続しないようにするにはどうすればよいですか?私はset -eこの行動が実施されることを期待したでしょう。

答え1

set -eif//設定の条件付き部分、aの左側、または関数内、サブシェル、ソースファイル、これらの条件で呼び出されるedコードなど、条件として使用されたコマンドが失敗した場合は実行されません。whileuntil||&&eval

これが実際にそうであれば:

if [ ! -f /custom.log ]; then

/custom.log通常のファイルの場合、スクリプトは終了し、[ゼロ以外の終了状態で終了します。

テスト条件が満たされず、構文エラーがある場合(ただし、すべての構文エラーではない、例えばinではない)、シェルの組み込み[コマンド(bashおよび他のほとんどの実装)は状態で終了します。12[ -v 'a[+]' ]POSIXでは、エラーが発生した場合に備えて終了状態が1より大きくなければなりません。

したがって、コマンドが 1 より大きいコードで終了した場合は、条件付きであるかどうかにかかわらずスクリプトを終了することを選択できます。たとえば、次のようになります。

shopt -s extdebug # make sure the DEBUG trap propagates to subshells
trap '(($?>1 && (ret=$?))) && exit "$ret"' DEBUG
[ -f / ] || echo / not a regular file # OK
[ -f /] || echo was a syntax error # causes an exit, not output
echo not reached

トラップはシャットダウンをトリガするのと同じ条件でのみ実行されるため、ERRトラップは使用できません。ERRset -e

さて、その意味を知ってください。たとえば、結果は次のようになります。

if grep -qs pattern /file; then
  echo pattern was found in /file
fi

/file存在しない、または読み取れない場合は終了します。grepこの場合、戻り状態は 2 です。たとえ使用されても、-s明らかにこれらのケースを無視する意図です。

したがって、条件に使用するコマンドが1より大きい状態で終了できる条件を知っておく必要があります。これらの問題を解決するには、次のものが必要です。

if sh -c 'grep -sq pattern / file || exit 1'; then...

制限することができます終了ステータスが1より大きい場合は終了します。[orコマンドに接続しますtest。たとえば、次のようになります。

unset -v previous_BASH_COMMAND
trap '
  case $previous_BASH_COMMAND in
    ("[ "* | "test "*) (($?>1 && (ret=$?))) && exit "$ret"
  esac
  previous_BASH_COMMAND=$BASH_COMMAND' DEBUG

これにはいくつかの制限があります。存在する

echo x
([ -f/]; echo y)

これによりサブシェルは終了しますが、親シェルは$previous_BASH_COMMANDそこに設定されていないため終了しません。そして:

[ -f / ] && echo a regular file
(grep -qs foo /file && echo foo in /file)
echo here

willは2でwasなのでecho here、実行中にシェルが終了します。$?$previous_BASH_COMMAND[ -f / ]

とにかく、

[ -f /] | cat
export var="$([ -f /])"

終了状態が親シェルプロセスに伝播されないため、終了状態を検出できません(pipefail最初の場合のオプションを除く)。

バグは開発時(スクリプトの作成とテスト時に)簡単に検出されるため、実行時にこの(脆弱な)検出を追加する価値があるかどうかはわかりません。

関連情報