子 shell 可见父陷阱但 运行

Parent trap visible but not run by subshell

测试 Bash 5.0.2

根据 GNU Bash Reference Manual

Bash performs the expansion [of a command substitution] by executing [the] command in a subshell environment

根据The Open Group Base Specifications Issue 6

when a subshell is entered, traps that are not being ignored are set to the default actions.


所以当运行使用以下脚本时:

function a {
   trap -p EXIT
}

trap "echo 'parent'" EXIT

echo "$(a)"
(a)

trap - EXIT

echo 'exiting'

...我希望输出为:

exiting

...但是我得到:

trap -- 'echo '\''parent'\''' EXIT
trap -- 'echo '\''parent'\''' EXIT
exiting

... 意味着函数 a - 尽管它在子 shell 中是 运行 - 正在查看父 shell 的陷阱命令(通过 trap -p)但不执行它们。


这是怎么回事?

您似乎在阅读规范的旧版本。在most recent one,

When a subshell is entered, traps that are not being ignored shall be set to the default actions, except in the case of a command substitution containing only a single trap command, when the traps need not be altered.

您会注意到陷阱完全按照规格触发。只是 trap 的输出出乎意料。

这是 Bash 4.2 (release notes) 中的一个功能:

b.  Subshells begun to execute command substitutions or run shell functions or
    builtins in subshells do not reset trap strings until a new trap is
    specified.  This allows $(trap) to display the caller's traps and the
    trap strings to persist until a new trap is set.

通常情况下,人们会认为这是理所当然的。考虑一下这个完全不足为奇的 Bash 交换:

bash$ trap
trap -- 'foo' EXIT
trap -- 'bar' SIGINT

bash$ trap | grep EXIT
trap -- 'foo' EXIT

现在看看在 Dash、Ksh 或 Zsh 等其他 shell 中的结果:

dash$ trap
trap -- 'foo' EXIT
trap -- 'bar' INT

dash$ trap | grep EXIT
(no output)

这也许更正确,但我怀疑很多人会期望它。