web-dev-qa-db-ja.com

「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はこの動作を強制します。

7

set -eは、if/while/until構成要素の条件セクションや||&&の左側などの条件として使用される失敗したコマンドではトリガーされません。関数、サブシェル、ソースファイル、evaledコードで、これらの条件下で呼び出されます。

もしそうなら、それから:

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

/custom.logもゼロ以外の終了ステータスで終了するため、[が通常のファイルである場合はスクリプトを終了します。

bashシェル(および他のほとんどの実装)の[組み込みコマンドは、テストされた条件が満たされていない場合は1ステータスで終了し、構文エラーがある場合は2で終了します(ただし、すべての構文エラーではありませんが、 [ -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トラップは、set -eによって終了をトリガーする条件と同じ条件でのみ実行されるため、そのためにERRトラップを使用できないことに注意してください。

ここで、その影響に注意してください。例えば、それは以下を引き起こします:

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以上の終了ステータスで終了[または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

echo hereが2で、$?$previous_BASH_COMMANDだったため、[ -f / ]を実行するとシェルが終了します。

とにかく、

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

終了ステータスが親シェルプロセスに伝達されないため、検出できませんでした(最初のケースのpipefailオプションを除く)。

現在、開発時に(スクリプトを記述してテストするときに)エラーを簡単に検出できる場合、実行時にこの種の(もろい)検出を追加するのに問題があるかどうかはわかりません。

10