根据man bash
,
set -e
如果(剪断)立即退出。
ERR
上的陷阱(如果已设置)在外壳程序退出之前执行。
但是,下面的脚本不会调用ERR
陷阱。
trap 'echo ERR; sleep 1' ERR
trap 'echo EXIT; sleep 1' EXIT
set -e
array=(a b c)
echo $(( ${#array[@] - 1)) #note the closing bracket } is forgotten
echo "after"
预期结果是
$ bash script.sh
4.sh: line 7: ${#array[@] - 1: bad substitution
ERR
EXIT
# and then shell exits
实际结果是
$ bash script.sh
4.sh: line 7: ${#array[@] - 1: bad substitution
EXIT
# and then shell exits
如果我删除set -e
行,那么
$ bash script2.sh
4.sh: line 7: ${#array[@] - 1: bad substitution
after #the last echo command is executed
EXIT
这意味着set -e
捕获语法错误。为什么在外壳程序退出时退出ERR
陷阱?
环境:
我在两台计算机上测试了脚本。
$ bash --version
GNU bash, version 4.4.12(1)-release (arm-unknown-linux-gnueabihf)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ bash --version
GNU bash, version 5.0.11(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
补充:
根据发布的答案,很自然不会执行陷阱,这是因为echo $(( ${#array[@] - 1))
尚未完成其执行以返回退出状态。我的理解正确吗?
但是,man bash
将set -e
解释为
如果管道(可能由一个简单命令组成),列表或复合命令(请参见上面的SHELL GRAMMAR)以非零状态退出,则立即退出。
我认为这也需要命令来完成。如果echo $(( ${#array[@] - 1))
未完成执行,我相信set -e
在这里不起作用,应该执行echo "after"
。
补充2:
根据oguz ismail's comment,这是一个文档问题。 POSIX说set -e
应该在以下情况退出外壳程序
返回非零退出状态(如您在man bash
中看到的那样)
或发生外壳错误
和shell error includes syntax error。但是man bash
缺少第二种情况的解释,对吗?如果是这样,则除了man bash
不能准确解释实现的事实和陈述“这些是errexit
(-e
)选项所遵循的相同条件之外,其他所有内容都是一致的。可以在trap
的{{1}}的说明中找到。
答案 0 :(得分:2)
这是由于多年来如何在set -e
中实现bash
的一种怪异方式。它伴随着许多预期会起作用的特定案例。
来自man bash
:
trap ... ERR
while
或until
关键字之后的命令列表的一部分,则不会执行 ERR 陷阱... if
语句中测试的一部分... &&
或||
列表中执行的命令的一部分,除了最后&&
或||
之后的命令... !
反转了命令的返回值。-e
选项所遵循的相同条件。此示例代码中的内容为shell expansion error,上述条款从不确实适用。在上述所有条款中,错误的命令会运行/完成并完成设置,并设置将返回代码返回到外壳,以触发您的ERR
陷阱。
但是,当遇到行echo $(( ${#array[@] - 1))
时,算术扩展失败后不久,便没有实际运行的命令触发ERR
陷阱,因为要触发陷阱,该命令需要完成。在man bash
页上
Bash
正在等待命令完成并接收到已设置陷阱的信号,则在命令完成之前将不执行陷阱。答案 1 :(得分:1)
ERR
陷阱从未陷阱语法错误,并且不是为此目的而设计的。来自man bash
:
If a sigspec is ERR, the command arg is executed whenever a
pipeline (which may consist of a single simple command), a list,
or a compound command returns a non-zero exit status ...
在这种情况下,命令从不执行,因此不会返回非零状态,脚本在此之前会失败。