语法错误

时间:2019-10-10 07:13:23

标签: bash bash-trap

根据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 bashset -e解释为

  

如果管道(可能由一个简单命令组成),列表或复合命令(请参见上面的SHELL GRAMMAR)以非零状态退出,则立即退出。

我认为这也需要命令来完成。如果echo $(( ${#array[@] - 1))未完成执行,我相信set -e在这里不起作用,应该执行echo "after"


补充2:

根据oguz ismail's comment,这是一个文档问题。 POSIXset -e应该在以下情况退出外壳程序

  • 返回非零退出状态(如您在man bash中看到的那样)

  • 发生外壳错误

shell error includes syntax error。但是man bash缺少第二种情况的解释,对吗?如果是这样,则除了man bash不能准确解释实现的事实和陈述“这些是errexit-e)选项所遵循的相同条件之外,其他所有内容都是一致的。可以在trap的{​​{1}}的说明中找到。

2 个答案:

答案 0 :(得分:2)

这是由于多年来如何在set -e中实现bash的一种怪异方式。它伴随着许多预期会起作用的特定案例。

来自man bash

  • trap ... ERR
    • 如果sigspec是 ERR ,则每当管道(可能由一个简单命令组成)时,将执行命令 arg 。列表,或者复合命令返回非零退出状态,但要满足以下条件:
      • 如果失败的命令是紧随whileuntil关键字之后的命令列表的一部分,则不会执行 ERR 陷阱...
      • ... if语句中测试的一部分...
      • ...在&&||列表中执行的命令的一部分,除了最后&&||之后的命令...
      • ...管道中除最后一条之外的任何命令...
      • ...或者如果使用!反转了命令的返回值。
    • 这些是 errexit -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 ...

在这种情况下,命令从不执行,因此不会返回非零状态,脚本在此之前会失败。

相关问题