尽管set -e和/或trap处于活动状态,但未捕获Bash退出状态

时间:2019-11-06 10:55:35

标签: bash

有人可以在下面的代码片段中解释bash / set -e行为吗?

#!/bin/bash

# Comment if you want to test the trap only
set -e -o pipefail -u -E

# Comment if you want to test the set -e only
trap "echo ERROR CAUGHT; exit 12" ERR

function reproduce() {
    # Trigger arithmetic error on purpose
    a=$((1109962735 - hello=12272 + 1))
}

reproduce

# The script is expected to trigger the trap and/or activate the set -e. In both cases it should stop and exit here on error.

status_code=$?
echo "STATUS ${status_code}"
if [[ "${status_code}" != "0" ]];then
    echo "FIXME: why was status code not caught by set -e ?"
    echo "Executing false to prove set -e is still active"
    false
    # If the following is not executed then it proves -e is still active
    echo "set -e not active !!!!!"
    exit 2
fi

执行以下命令可获得的信息:

$ bash reproduce.sh
reproduce.sh: line 8: 1109962735 - hello=12272 + 1: attempted assignment to non-variable (error token is "=12272 + 1")
STATUS 1
FIXME: why was status code it not caught by set -e ?
Executing false to prove set -e is still active
ERROR CAUGHT

检查退出代码

$ echo $?
1

bash版本

bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)

也转载于

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)

与注释相关的其他注释(无论如何都要感谢所有建议):

  • 评论陷阱不会改变观察到的怪异行为
  • 删除set -e只保留陷阱会触发陷阱

2 个答案:

答案 0 :(得分:3)

让我们简化一下;所需的最小代码量 重现您正在处理的问题

set -e
: $((+)) # arithmetic expansion error
echo survived

根据标准,此内容永远不要打印survivedit says a POSIX shell running non-interactively shall immediately exit upon an expansion error。但是貌似Bash并不这么认为。尽管此差异未在手册页中明确记录,但在description of POSIX mode中指出

  
      
  1. 如果算术中的语法错误,则非交互式shell退出   扩展会导致表达式无效。
  2.   

我们可以说这意味着在其默认操作模式下,非交互式Bash会话不会在发生此类错误时退出,但是正如您所意识到的那样,它也不触发errexit机制或ERR陷阱。相反,它会为$?继续前进分配一个非零值。

要克服此问题并获得预期的行为,您应按以下方式定义reproduce

function reproduce() (
    # Trigger arithmetic error on purpose
    a=$((1109962735 - hello=12272 + 1))
)

这样,扩展错误将在子外壳中发生,并使其以非零状态退出,因此errexit和trap将能够捕获该错误。


根据破折号-o的请求,此版本可在表达式有效时为当前执行环境设置a

function reproduce() {
    if : $((expression)); then
        a=$((expression))
    fi
}

答案 1 :(得分:2)

从表面上看,bash似乎不会因各种SYNTAX错误而触发陷阱。仅当执行命令(外部,内置)(并返回非零)时,才会触发ERR陷阱。

在手册页中:

  

如果sigspec为ERR,则每当管道执行命令arg   (可能由一个简单的命令组成),列表或复合   命令返回一个非零的退出状态,但必须遵守以下条件   条件...

ERR陷阱仅适用于 PIPELINE 。如果bash识别出语法错误,它将在执行管道之前中止,因此没有陷阱。即使他的'-e'文档指定了相同的条件(if a pipeline ... exit with non-zero status),但观察到的行为还是不同的。

如果您尝试其他扩展-例如命令扩展-陷阱被触发,因为有管道执行:

  • a = $(错误命令)
  • a = $([)

如果在算术扩展中使用try各种语法错误,则不会触发陷阱-没有管道。

  • a = $((2 +))
  • a = $(((2 @))

此外,其他bash语法错误也不会触发陷阱:()[[ ]]

我找不到不需要对源脚本进行大量更改的解决方案。可能会向bash团队提出错误/功能请求?