以下脚本存在语法错误或某种类型的错误:
#!/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
构造的条件部分或 a 的左侧,或者在这些条件下调用的函数、子 shell、源文件、ed 代码中。while
until
||
&&
eval
如果确实如此,那么:
if [ ! -f /custom.log ]; then
/custom.log
如果是常规文件,则将退出脚本,[
然后也会以非零退出状态退出。
如果不满足测试条件,并且存在语法错误(但并非所有语法错误,例如,不在 中), shell[
的内置命令(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
如果不存在或不可读则退出,grep
在这种情况下返回状态为 2,即使使用-s
,其意图显然是忽略这些情况。
因此,您需要注意在哪些条件下您在条件中使用的命令可能会以大于 1 的状态退出。要解决这些问题,您需要类似以下内容:
if sh -c 'grep -sq pattern / file || exit 1'; then...
您可以限制退出状态大于 1 时退出到[
ortest
命令,例如:
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)
这将导致子 shell 退出,但不会导致父 shell 退出,因为$previous_BASH_COMMAND
尚未在那里设置。并在:
[ -f / ] && echo a regular file
(grep -qs foo /file && echo foo in /file)
echo here
shell 将在运行时退出echo here
,因为$?
will 是 2 并且$previous_BASH_COMMAND
was [ -f / ]
。
无论如何,像这样的事情
[ -f /] | cat
export var="$([ -f /])"
无法检测到退出状态,因为退出状态不会传播到父 shell 进程(pipefail
第一种情况下的选项除外)。
现在,我不确定是否值得在运行时添加这种(脆弱的)检测,因为在开发时(编写和测试脚本时)很容易检测到错误。