繁体   English   中英

Bash 尽管设置 -e 和/或陷阱处于活动状态,但未捕获退出状态

[英]Bash exit status not caught despite set -e and/or trap being active

有人可以解释下面代码片段中的 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 以仅保留陷阱确实会触发陷阱

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

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

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

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

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

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

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

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


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

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

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

从手册页:

如果 sigspec 是 ERR,则只要管道(可能由单个简单命令组成)、列表或复合命令返回非零退出状态,就会执行命令 arg,但需满足以下条件...

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

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

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM