Here is minimal code for issue demonstration: http://pastebin.com/5TXDpSh5
#!/bin/bash
set -e
set -o pipefail
function echoTraps() {
echo "= on start:"
trap -p
trap -- 'echo func-EXIT' EXIT
echo "= after set new:"
trap -p
# we can ensure after script done - file '/tmp/tmp.txt' was not created
trap -- 'echo SIG 1>/tmp/tmp.txt' SIGPIPE SIGHUP SIGINT SIGQUIT SIGTERM
}
trap -- 'echo main-EXIT1' EXIT
echo "===== subshell trap"
( echoTraps; )
echo "===== pipe trap"
echoTraps | cat
echo "===== done everything"
output
===== subshell trap
= on start:
= after set new:
trap -- 'echo func-EXIT' EXIT
func-EXIT
===== pipe trap
= on start:
= after set new:
trap -- 'echo func-EXIT' EXIT
===== done everything
main-EXIT1
expected output
===== subshell trap
= on start:
= after set new:
trap -- 'echo func-EXIT' EXIT
func-EXIT
===== pipe trap
= on start:
= after set new:
trap -- 'echo func-EXIT' EXIT
func-EXIT <---- here is the expected difference
===== done everything
main-EXIT1
NB: i tested for OSX 10.9.2 bash (3.2.51) - other versions of bash has same difference between actual an expected output, and described bellow
Here are some more test cases for your amusement:
bash-4.2.25(1)
bash-4.3.0(1)
Bottom line: Don't rely on edge-cases like this. I remember investigating other inconsistencies (not about traps) with regards to subshells and pipes and tried to wrap my head around the
bash
source code and you don't want to go down that route and try to understand why it behaves like it does in certain situations (the code is really horrible, btw). As you can see, some things seem to have been "fixed" and/but both my examples already behave differently than yours.The only way to find out if this behavior is expected or not is to ask Chet Ramey (GNU bash maintainer). Please send an email with your report to bug-bash@gnu.org
You can see that the current behavior seems to be correct, given that it handles the subshell case explicitly in:http://git.savannah.gnu.org/cgit/bash.git/tree/execute_cmd.c#n621
As you can see, the explicit subshell case is handled as a special case (and so is the case with the command grouping). This behavior has evolved historically, as Adrian found out, due to multiple bug reports.
This is the list of changes for this particular feature (triggering EXIT trap on subshells):
Commit: http://git.savannah.gnu.org/cgit/bash.git/commit/?id=a37d979e7b706ce9babf1306c6b370c327038eb9
Report: https://lists.gnu.org/archive/html/bug-bash/2013-04/msg00126.html (Re: trap EXIT in piped subshell not triggered during wait)
Commit: http://git.savannah.gnu.org/cgit/bash.git/commit/?id=1a81420a36fafc5217e770e042fd39a1353a41f9
Report: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=698411 (EXIT trap and pipeline and subshell)
Commit: http://git.savannah.gnu.org/cgit/bash.git/commit/?id=fd58d46e0d058aa983eea532bfd7d4c597adef54
Report: http://lists.gnu.org/archive/html/bug-bash/2012-07/msg00084.html (EXIT traps in interactive shells)
There is also a recent bug report in relation to the EXIT trap not executing in some expected contexts: http://lists.gnu.org/archive/html/bug-bash/2016-11/msg00054.html