3

In this script We have a script trap.sh

#!/bin/bash
trap "echo trapped" EXIT
exit 0

and test.sh. If test.sh is like

#!/bin/bash
. trap.sh

or

#!/bin/bash
./trap.sh | :

trap works

But if test.sh is like

#!/bin/bash
. trap.sh | :

the trap didn't work.

Anybody know why is this?

Jolta
  • 2,620
  • 1
  • 29
  • 42
dspjm
  • 5,473
  • 6
  • 41
  • 62

3 Answers3

1

Better change your test command to . trap.sh|cat (standard output from trap.sh couldn't be displayed with :). But even then there is no output, so you are right: the trap didn't work. This must be a bug in bash, and should be reported to the maintainers.

Interestingly, when we echo $$ from inside the script trap.sh, we see that it is executed by the same shell that executes the whole pipeline . trap.sh|cat, contradictory to the manual's statement: Each command in a pipeline is executed as a separate process (i.e., in a subshell). This was a fallacy, see comments. Perhaps this is related to some optimization to minimize subshell creation, but that's just speculation.

Armali
  • 18,255
  • 14
  • 57
  • 171
  • 1
    `$$` always evaluates to the process ID of the parent shell, even if the child executes in a different process. – chepner Oct 15 '13 at 13:12
  • +1, as I think I agree this might be a bug. I replaced the echo with `touch trap.fired`, to make sure this wasn't an issue with standard output just being lost somewhere. With `. trap.sh | :`, the file was not created. – chepner Oct 15 '13 at 13:42
  • Although the bash manual states: _$ Expands to the process ID of the shell_, you are evidently right that `$$` in this case evaluates to the process ID of the parent shell. Thanks to your comment, I discovered the shell variable *BASHPID*, about which the manual says: _Expands to the process id of the current bash process. This differs from $$ under certain circumstances, such as subshells that do not require bash to be re-initialized._ Indeed, `echo $BASHPID` shows different values. – Armali Oct 16 '13 at 08:17
1

I modified trap.sh to include the xtrace option.

#!/bin/bash
set -x
trap 'echo trapped' EXIT
exit 0

Running trap.sh as a script produces

~ $ ./trap.sh | cat + trap 'echo trapped' EXIT + exit 0 + echo trapped trapped

Sourcing it first, however produces

~ $ . trap.sh | cat
++ trap 'echo trapped' EXIT
++ exit 0

This indicates that the trap is executed in a deeper subshell (why, I don't know), and that the trap itself is never executed (I confirmed in a second experiment by touching a file int he trap instead of just echoing, in case there was an issue with standard output being inherited; the file was never touched).

My guess is that somehow the EXIT signal is being ignored prior to the source command being executed, based on this sentence from the description of the trap command in the man page:

Signals ignored upon entry to the shell cannot be trapped or reset.

As a result, the trap command is executed, but the trap itself is never registered, and so does not fire.

chepner
  • 497,756
  • 71
  • 530
  • 681
0

The command on the left hand side of a pipe is run in a subshell:

exit | grep

The exit sigtrap seems not to be propagated to subshells.

trap 'echo T >&2' EXIT ; (exit)    # Nothing.
choroba
  • 231,213
  • 25
  • 204
  • 289
  • I noticed the subshell problem. But when we call . trap.sh | :, what I think is we call trap.sh in a subshell, and the subshell called trap, when the subshell exits, trap call should be invoked like we call ./trap.sh | :. I didn't see why there are different outcomes. – dspjm Jul 23 '13 at 02:27