man bash
包括此文档以供使用trap
:
trap [-lp] [[arg] sigspec ...]
…
The ERR trap is not executed if the failed command is part of the command list
immediately following a while or until keyword, part of the test in an if statement,
part of a && or || list, or if the command's return value is being inverted via !.
These are the same conditions obeyed by the errexit option.
…
我明白那个“&& 或 || 的一部分列表”意味着如果命令是具有这些控制运算符的列表的一部分,即使它具有非零退出状态,它不应发送 ERR 信号(或者如果使用则退出脚本set -o errexit
)。
然而,这里有一个测试脚本似乎与此相矛盾:
#!/usr/bin/env bash
_trap_err() {
local status=$? sig=$1 line=$2;
echo "Exit status ${status} on line ${line}: \`${BASH_COMMAND}\`";
}
trap '_trap_err ERR $LINENO' ERR;
function control_operators() {
# The next line will send an ERR signal.
[[ 1 -eq 2 ]] && echo Hello;
}
control_operators;
# The next line will not send an ERR signal.
[[ 1 -eq 2 ]] && echo Hello;
echo Done;
输出是:
❯ test.sh
Exit status 1 on line 14: `[[ 1 -eq 2 ]]`
Done
因为[[ 1 -eq 2 ]]
是“&& 或 || 的一部分”列出”它不应触发 ERR 信号。
预期输出是:
❯ test.sh
Done
此外,仅列出清单[[ 1 -eq 2 ]] && echo Hello
里面该control_operators
函数发送 ERR 信号,但[[ 1 -eq 2 ]] && echo Hello
外部该函数没有。
该脚本环境中设置的选项是(输出set -o | grep 'on$'
):
braceexpand on
hashall on
interactive-comments on
xtrace on
问题:
- 这种行为是否正常,或者我是否在列表中错误地使用了控制运算符(或错误地解释了文档)?
- 当命令是条件表达式的一部分并且实际上并不构成错误状态时,避免触发 ERR 陷阱的最佳方法是什么?
- 一种选择是使用控制运算符添加
|| true
到每个列表的末尾。 - 另一种选择是使用速记
if [[ 1 -eq 2 ]]; then echo Hello; fi
代替&&
;这不会导致发送 ERR 信号,因为(根据man bash
)“if 语句中测试的一部分”——尽管我很困惑为什么当“&& 或 || 的一部分”时这会起作用。列表”没有。
- 一种选择是使用控制运算符添加
- 为什么
[[ 1 -eq 2 ]]
只有在函数内部时才发送 ERR 信号?
更新:@Kusalananda 的第一个答案对于上面的示例是正确的(添加到函数return 0
中control_operators
可以防止 ERR 信号)。然而,这是另一个例子,它似乎违反了“&& 或 || 的一部分”列表”规则:
#!/usr/bin/env bash
_trap_err() {
local status=$? sig=$1 line=$2;
echo "Exit status ${status} on line ${line}: \`${BASH_COMMAND}\`";
}
trap '_trap_err ERR $LINENO' ERR;
true && false;
echo Done;
该脚本的输出是:
❯ test.sh
Exit status 1 on line 7: `false`
Done
该行true && false
不应发送 ERR 信号,因为该命令false
是“&& 或 || 的一部分”列表”。那么,为什么这么做呢?
更新2:我正在使用 bash 3.2 在 macOS 上工作,这就是上面的代码片段man bash
没有提及的原因“除了最后的 && 或 || 后面的命令”。
答案1
ERR
由于调用的返回状态control_operators
非零,您的脚本正在触发陷阱。函数中的测试没有直接地触发陷阱。
陷阱输出通过打印的行号来指示这一点,该行号将是调用该函数的行。
陷阱输出还指示这个非零退出状态来自哪里,这是函数中测试的结果。由于测试是函数中执行的最后一件事,因此这设置了函数的退出状态。
该函数调用不是 AND 或 OR 列表的一部分,因此会触发陷阱。
对于您更新的问题,您忘记了ERR
如果返回非零退出状态的命令是,陷阱仍然会被调用最后的AND 或 OR 列表中的命令。
从bash
手册中,我强调:
ERR
如果失败的命令是紧随while
oruntil
关键字的命令列表的一部分、语句中测试的一部分、或列表if
中执行的命令的一部分,则不会执行陷阱&&
||
&&
除了最后一个或之后的命令||
、管道中除最后一个命令之外的任何命令,或者如果使用 反转命令的返回值!
。
您的示例是true && false
,并且由于是false
AND 列表中的最后一个,因此它会触发ERR
陷阱。
另一种说法是,ERR
如果命令决定退出状态列表或管道的返回非零退出状态(如果!
与该命令一起使用,则返回零退出状态)。