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

rap*_*lon 8 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
Run Code Online (Sandbox Code Playgroud)

这是执行时获得的结果:

$ 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
Run Code Online (Sandbox Code Playgroud)

检查退出代码

$ echo $?
1
Run Code Online (Sandbox Code Playgroud)

重击版本

bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Run Code Online (Sandbox Code Playgroud)

转载也

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
Run Code Online (Sandbox Code Playgroud)

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

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

ogu*_*ail 3

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

set -e
: $((+)) # arithmetic expansion error
echo survived
Run Code Online (Sandbox Code Playgroud)

根据标准,这不应该打印survived它表示非交互运行的 POSIX shell 在出现扩展错误时应立即退出。但 Bash 似乎并不这么认为。虽然手册页中没有明确记录这种差异,但在POSIX 模式的描述中它说

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

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

为了克服这个问题并获得预期的行为,您应该定义reproduce如下

function reproduce() (
    # Trigger arithmetic error on purpose
    a=$((1109962735 - hello=12272 + 1))
)
Run Code Online (Sandbox Code Playgroud)

这样,扩展错误将发生在子 shell 中,并导致它以非零状态退出,因此 errexit 和 trap 将能够捕获它。


a根据 dash-o 的要求,这里是当表达式有效时为当前执行环境设置的版本

function reproduce() {
    if : $((expression)); then
        a=$((expression))
    fi
}
Run Code Online (Sandbox Code Playgroud)