根据man bash
,
set -e
Exit immediately if (snip). A trap on
ERR
, if set, is executed before the shell exits.
但是,下面的脚本不会调用 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
陷阱没有被调用,尽管 shell 确实退出了?
环境:
我在两台机器上测试了脚本。
$ 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
as
Exit immediately if a pipeline (which may consist of a single simple command), a list, or a compound command (see SHELL GRAMMAR above), exits with a non-zero status.
我觉得这也是需要命令来完成的。如果 echo $(( ${#array[@] - 1))
没有完成它的执行,我相信 set -e
不应该在这里工作并且 echo "after"
应该被执行。
补充2:
根据 oguz ismail's comment ,这是一个文档问题。 POSIX说 set -e
应该在什么时候退出 shell
返回非零退出状态(如您在
man bash
中所见)或发生shell错误
和shell error includes syntax error .但是 man bash
缺少对第二种情况的解释,对吗?如果是这样,除了 man bash
没有准确解释实现和声明“这些是 errexit
遵守的相同条件(-e
) 选项。”在 man bash
中的 trap
解释中找到。
最佳答案
这是因为多年来 bash
中 set -e
的实现方式很奇怪。它伴随着许多预期会起作用的特定案例出现。
来自 man bash
:
陷阱...错误
- 如果一个 sigspec 是 ERR,则命令 arg 会在管道 (可能由单个简单命令组成) 时执行,一个列表或复合命令返回非零退出状态,但要满足以下条件:
- 如果失败的命令是紧跟在
while
或until
关键字之后的命令列表的一部分,则不会执行 ERR 陷阱... - ...
if
语句中的部分测试... - ...在
&&
或||
列表中执行的命令的一部分,除了最后的&&
或之后的命令||
... - ...管道中的任何命令,但最后...
- ...或者如果使用
!
反转命令的返回值。
- 如果失败的命令是紧跟在
- errexit
-e
选项遵循相同的条件。
- 如果一个 sigspec 是 ERR,则命令 arg 会在管道 (可能由单个简单命令组成) 时执行,一个列表或复合命令返回非零退出状态,但要满足以下条件:
您在此示例代码中拥有的是 shell expansion error上述条款从不真正适用。在上述所有子句中,不正确的命令运行/完成并设置一个返回码返回给您的 ERR
陷阱的 shell。
但是当遇到 echo $(( ${#array[@] - 1))
行时,在算术扩展失败后不久,没有实际运行的命令会触发ERR
陷阱,因为要触发陷阱,命令需要完整。从 man bash
页面
- 如果
Bash
正在等待命令完成并接收到已为其设置陷阱的信号,则在命令完成之前不会执行陷阱。
关于bash - `ERR` 语法错误时不执行trap,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58317346/