Tested for Bash 5.0.2
According to the GNU Bash Reference Manual,
Bash performs the expansion [of a command substitution] by executing [the] command in a subshell environment
According to The Open Group Base Specifications Issue 6:
when a subshell is entered, traps that are not being ignored are set to the default actions.
So when running the following script:
function a {
trap -p EXIT
}
trap "echo 'parent'" EXIT
echo "$(a)"
(a)
trap - EXIT
echo 'exiting'
... i would expect an output of:
exiting
... but instead I get:
trap -- 'echo '\''parent'\''' EXIT
trap -- 'echo '\''parent'\''' EXIT
exiting
... meaning that the function a
- eventhough it is being run in a subshell - is seeing the the parent shell's trap commands (via trap -p
) but not executing them.
What is going on here?
You'll notice that the traps trigger exactly according to spec. It's just the output from trap
that's unexpected.
This is a feature in 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.
Normally, people would take this for granted. Consider this totally unsurprising Bash exchange:
bash$ trap
trap -- 'foo' EXIT
trap -- 'bar' SIGINT
bash$ trap | grep EXIT
trap -- 'foo' EXIT
Now look at the result in other shells like Dash, Ksh or Zsh:
dash$ trap
trap -- 'foo' EXIT
trap -- 'bar' INT
dash$ trap | grep EXIT
(no output)
This is perhaps more correct, but I doubt many people would expect it.