次のスクリプトには構文エラーまたは何らかのエラーがあります。
#!/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 -e
if
//構文の条件セクションや の左側などの条件として使用される失敗したコマンド、while
またはそれらの条件下で呼び出される関数、サブシェル、ソースファイル、 ed コードではトリガーされません。until
||
&&
eval
もしそうなら、次のようになります。
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
ため、そのためにはトラップを使用できないことに注意してください。ERR
set -e
さて、その影響には注意してください。たとえば、次のようなことが起こります。
if grep -qs pattern /file; then
echo pattern was found in /file
fi
/file
が存在しなかったり読み取り可能でなかったりした場合には、 は 2 ステータスを返すため、終了しますgrep
。ただし、 では-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
は 2 で は であるecho here
ため、を実行するとシェルは終了します。$?
$previous_BASH_COMMAND
[ -f / ]
いずれにしても、
[ -f /] | cat
export var="$([ -f /])"
終了ステータスが親シェルプロセスに伝播されないため、検出できませんでした (pipefail
最初のケースのオプションを除く)。
さて、開発時(スクリプトを書いてテストするとき)にエラーが簡単に検出できる場合、実行時にこの種の(脆弱な)検出を追加する手間をかける価値があるかどうかはわかりません。