Search code examples
bashsubshellcommand-substitutionbash-trap

Parent trap visible but not run by subshell


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?


Solution

  • 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.