bash - `ERR` 语法错误时不执行trap

标签 bash bash-trap

根据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 ,这是一个文档问题。 POSIXset -e 应该在什么时候退出 shell

  • 返回非零退出状态(如您在 man bash 中所见)

  • 发生shell错误

shell error includes syntax error .但是 man bash 缺少对第二种情况的解释,对吗?如果是这样,除了 man bash 没有准确解释实现和声明“这些是 errexit 遵守的相同条件(-e) 选项。”在 man bash 中的 trap 解释中找到。

最佳答案

这是因为多年来 bashset -e 的实现方式很奇怪。它伴随着许多预期会起作用的特定案例出现。

来自 man bash:

  • 陷阱...错误
    • 如果一个 sigspec 是 ERR,则命令 arg 会在管道 (可能由单个简单命令组成) 时执行,一个列表或复合命令返回非零退出状态,但要满足以下条件:
      • 如果失败的命令是紧跟在 whileuntil 关键字之后的命令列表的一部分,则不会执行 ERR 陷阱...
      • ...if 语句中的部分测试...
      • ...在 &&|| 列表中执行的命令的一部分,除了最后的 && 之后的命令||...
      • ...管道中的任何命令,但最后...
      • ...或者如果使用 ! 反转命令的返回值。
    • errexit -e 选项遵循相同的条件。

您在此示例代码中拥有的是 shell expansion error上述条款从不真正适用。在上述所有子句中,不正确的命令运行/完成并设置一个返回码返回给您的 ERR 陷阱的 shell。

但是当遇到 echo $(( ${#array[@] - 1)) 行时,在算术扩展失败后不久,没有实际运行的命令会触发ERR 陷阱,因为要触发陷阱,命令需要完整。从 man bash 页面

  • 如果 Bash 正在等待命令完成并接收到已为其设置陷阱的信号,则在命令完成之前不会执行陷阱。

关于bash - `ERR` 语法错误时不执行trap,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58317346/

相关文章:

makefile - 如何在 GNU make 中捕获错误和中断?

linux - 通过 ssh 发送 Ctrl_C

bash - bash 脚本中的双感叹号

bash - 值对于 base 来说太大了(错误标记是 "08")

linux - 什么时候处理信号以及为什么某些信息会卡住?

BASH 中断 SIGINT 上的阅读线

bash - 如何在 bash 和/或使用 awk 中的单个列中进行数学运算?

git - 发布的自动标记

macos - Bash脚本,监视文件夹,执行命令