Chr*_*ski 8 shell bash shell-script error-handling
以下脚本存在某种语法错误或错误:
#!/usr/bin/env bash
set -euo pipefail
if [ ! -f /custom.log]; then
echo "test"
fi
abcxyz
Run Code Online (Sandbox Code Playgroud)
脚本失败,输出为:
./test.sh: line 4: [: missing `]'
./test.sh: line 7: abcxyz: command not found
Run Code Online (Sandbox Code Playgroud)
我不关心如何修复此脚本,但是如果遇到此错误,我该如何阻止脚本继续运行?我原以为set -e
会强制执行这种行为。
Sté*_*las 12
set -e
不触发对未能被用作条件比如在条件部分的命令if
/ while
/until
构建体或在左边的一个||
,&&
或在功能,子shell,乳源文件,eval
编码被在这些条件下被调用。
如果是这样,那么:
if [ ! -f /custom.log ]; then
Run Code Online (Sandbox Code Playgroud)
如果/custom.log
是常规文件,[
则会退出脚本,然后也会以非零退出状态退出。
如果不满足测试条件,并且存在语法错误(但并非所有语法错误,例如,不是 in )[
,bash
shell(和大多数其他实现)的内置命令会以1
状态退出。POSIX 要求退出状态大于 1 以防出错。2
[ -v 'a[+]' ]
因此,如果命令以大于 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
Run Code Online (Sandbox Code Playgroud)
请注意,您不能ERR
为此使用陷阱,因为ERR
陷阱仅在与触发退出的条件相同的条件下运行set -e
。
现在,请注意其影响。例如,这将导致:
if grep -qs pattern /file; then
echo pattern was found in /file
fi
Run Code Online (Sandbox Code Playgroud)
如果/file
不存在或不可读,则退出,因为grep
在这种情况下返回 2 状态,即使使用-s
,其意图显然是忽略这些情况。
因此,您需要注意在哪些条件下您在条件中使用的命令可能会以大于 1 的状态退出。要解决这些问题,您需要类似以下内容:
if sh -c 'grep -sq pattern / file || exit 1'; then...
Run Code Online (Sandbox Code Playgroud)
你可以限制在退出状态大于出口比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
Run Code Online (Sandbox Code Playgroud)
这有一些限制。在
echo x
([ -f/]; echo y)
Run Code Online (Sandbox Code Playgroud)
这将导致子外壳退出,但不会导致父外壳退出,因为$previous_BASH_COMMAND
尚未在那里设置。在:
[ -f / ] && echo a regular file
(grep -qs foo /file && echo foo in /file)
echo here
Run Code Online (Sandbox Code Playgroud)
shell 将在运行时退出echo here
,因为$?
would be 2 和$previous_BASH_COMMAND
was [ -f / ]
。
在任何情况下,像
[ -f /] | cat
export var="$([ -f /])"
Run Code Online (Sandbox Code Playgroud)
无法检测到退出状态,因为退出状态不会传播到父 shell 进程(pipefail
第一种情况下的选项除外)。
现在,我不确定在运行时添加这种(脆弱的)检测是否值得,因为在开发时(编写和测试脚本时)很容易检测到错误。