如何确定是否为 BASH 子 shell 定义了 EXIT 陷阱?

如何确定是否为 BASH 子 shell 定义了 EXIT 陷阱?

下面是一个 BASH 脚本示例。

#!/bin/bash

exithdl() { 
    echo "in exithdl subshell=$BASH_SUBSHELL" >&2
}

trap exithdl EXIT

fun() {
    echo "in fun subshell=$BASH_SUBSHELL" >&2
    trap -p EXIT >&2
    echo "returned from fun"
}

echo "subshell=$BASH_SUBSHELL" >&2
trap -p EXIT >&2
echo "$(fun)" >&2
echo "exiting" >&2

执行时会产生以下输出。

subshell=0
trap -- 'exithdl' EXIT
in fun subshell=1
trap -- 'exithdl' EXIT
returned from fun
exiting
in exithdl subshell=0

子 shell 0 和 1 中的命令trap -p EXIT >&2显示定义的 EXIT 陷阱,但该exithdl函数仅在子 shell 0 退出后执行。有没有办法确定exithdlsubshel​​l 1退出后不会执行?换句话说,是否可以添加一个或多个命令,fun以便此函数可以确定子 shell 1 没有 EXIT 陷阱?

在示例中,为子 shell 0 定义了 EXIT 陷阱,但没有为子 shell 1 定义。该trap -p EXIT命令在两个子 shell 中执行时显示完全相同的输出。因此,我假设trap -p EXIT不能用于检查特定子 shell 是否存在陷阱。我承认该trap -p EXIT命令在执行时显示了 EXIT 陷阱的存在fun,但如果当 fun 返回时 EXIT 陷阱将执行,则不会显示。我错过了什么吗?

答案1

使用 Bash 3 时

答案取决于您使用的 bash 版本。例如,我的 Mac 安装了 macOS High Sierra,默认情况下使用GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17).在这种情况下,可以通过在trap -p EXIT >&2function 中的命令中添加括号来解决该问题fun,如下所示。

#!/bin/bash

exithdl() { 
    echo "in exithdl subshell=$BASH_SUBSHELL" >&2
}

trap exithdl EXIT

fun() {
    echo "in fun subshell=$BASH_SUBSHELL" >&2
    (trap -p EXIT >&2) # <--- See Here
    echo "returned from fun"
}

echo "subshell=$BASH_SUBSHELL" >&2
trap -p EXIT >&2
echo "$(fun)" >&2
echo "exiting" >&2

如下所示的结果输出不再将函数显​​示fun为具有EXIT陷阱。

subshell=0
trap -- 'exithdl' EXIT
in fun subshell=1
returned from fun
exiting
in exithdl subshell=0

此方法也适用于其他陷阱的子集。例如,此子集包括、和INT陷阱QUIT。但是,此子集不包括和其他陷阱。TERMCHLDHUP

使用 Bash 4 和 5 时

我还在 VirtualBox 中安装了 Ubuntu 18 和 19。这些操作系统默认分别使用GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu)GNU bash, version 5.0.11(1)-release (x86_64-pc-linux-gnu)。当执行具有上述更改的脚本时,结果与原始输出相同。但是,对于这些较新版本的 bash,可以通过在 function 中的trap命令之前添加一个命令来解决该问题,如下所示。trap -p EXIT >&2fun

#!/bin/bash

exithdl() { 
    echo "in exithdl subshell=$BASH_SUBSHELL" >&2
}

trap exithdl EXIT

fun() {
    echo "in fun subshell=$BASH_SUBSHELL" >&2
    trap -- KILL # <--- See Here
    trap -p EXIT >&2
    echo "returned from fun"
}

echo "subshell=$BASH_SUBSHELL" >&2
trap -p EXIT >&2
echo "$(fun)" >&2
echo "exiting" >&2

选择该trap -- KILL命令是因为KILL陷阱无法更改。这会产生与上图相同的输出。此方法也适用于其他陷阱。

结论

如果您想exitTrap为用于定义当前陷阱的命令设置一个变量(例如 )EXIT,请使用下面所示的脚本。这应该适用于 bash 3、4 和 5。

trap -- KILL
exitTrap="$( (trap -p EXIT) )"

相关内容