1

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?

Marcus Rossel
  • 3,196
  • 1
  • 26
  • 41
  • You appear to be using bash 3.2; if anything, later versions of `bash` move in the opposite direction, executing the trap for `echo "$(a)"` *and* `(a)`. (This remains true through the current release, 5.0.3.) – chepner Mar 22 '19 at 14:42
  • I tested it for Bash 3.2.57 and Bash 5.0.2. Same result for me. – Marcus Rossel Mar 22 '19 at 14:44
  • 1
    Hm, I don't see an inherited trap for `(a)` in 3.2.37. – chepner Mar 22 '19 at 14:53

2 Answers2

2

You appear to be reading an older version of the specification. In the 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.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • Actually, I'm not sure about this. It indicates *some* exception for command substitutions, but the example in the spec doesn't really match the current case, and it also doesn't explain why `(a)` inherits the trap as well. – chepner Mar 22 '19 at 14:51
2

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.

that other guy
  • 116,971
  • 11
  • 170
  • 194