当条件发生错误时,“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 -e不会在用作条件的命令失败时触发,例如//if构造的条件部分或 a 的左侧,或者在这些条件下调用的函数、子 shell、源文件、ed 代码中。whileuntil||&&eval

如果确实如此,那么:

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

/custom.log如果是常规文件,则将退出脚本,[然后也会以非零退出状态退出。

如果不满足测试条件,并且存在语法错误(但并非所有语法错误,例如,不在 中), shell[的内置命令(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为此使用陷阱,因为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_COMMANDwas [ -f / ]

无论如何,像这样的事情

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

无法检测到退出状态,因为退出状态不会传播到父 shell 进程(pipefail第一种情况下的选项除外)。

现在,我不确定是否值得在运行时添加这种(脆弱的)检测,因为在开发时(编写和测试脚本时)很容易检测到错误。

相关内容