14

Here's a simple script that sets up a temp dir in the current dir and a trap to delete it on exit.

#filename: script   
set -x  
trap 'rm -rf "$d"' exit
d=`TMPDIR=$PWD mktemp -d`
"$@"

If I do ksh script sleep 100 or bash script sleep 100 and interrupt it with, C-C, the trap gets executed and the directory is deleted. It doesn't work with dash. Why? Is this a bug or intended behavior?

2 Answers 2

21

zsh, pdksh (though not recent versions of mksh derived from that), yash, the Bourne shell behave like dash.

Only bash, ksh88, ksh93 and mksh behave otherwise.

The POSIX spec is not clear on what should be the correct behaviour, but there's nothing in there that says that the shell is allowed to override the default handler for the SIGINT (or other) signal.

It says EXIT trap action should be evaluated when exit is invoked, but AFAICT, it doesn't even say for instance if it should be evaluated when the shell exits as the result of set -e or set -u or error conditions like syntax errors or failing special builtins.

To be able to run EXIT trap upon reception of a signal, the shell would need to install a handler on that signal.

That's what ksh, mksh and bash do, but the list of signals they handle is different between all three implementations. The only signals common between all 3 seem to be INT, QUIT, TERM, ALRM and HUP.

If you want the EXIT trap to be run upon some signals, the portable way would be to handle those signals yourself:

trap 'exit 1' INT HUP QUIT TERM ALRM USR1
trap 'cleanup' EXIT

That approach however doesn't work with zsh, which doesn't run EXIT trap if exit is called from a trap handler.

It also fails to report your death-by-signal to your parent.

So instead, you could do:

for sig in INT QUIT HUP TERM ALRM USR1; do
  trap "
    cleanup
    trap - $sig EXIT
    kill -s $sig "'"$$"' "$sig"
done
trap cleanup EXIT

Now, beware though that if more signals arrive while you're executing cleanup, cleanup may be run again. You may want to make sure your cleanup works correctly if invoked several times and/or ignore signals during its execution.

17
  • zsh only behave like dash in non-interactive session, it does the same as bash and ksh in interactive session.
    – cuonglm
    Commented Nov 4, 2015 at 13:26
  • Nice solution! If I understand it correctly then it even preserves the special exit status codes of the different signals.
    – phk
    Commented Nov 4, 2015 at 14:24
  • 1
    Stéphane, is a process killing itself with the received signal (kill -s $sig "'"$$"' "$sig" above) a best practice in general, or is it only applicable with SIGINT (as seen at section 5 at SignalTrap )
    – iruvar
    Commented Nov 11, 2018 at 3:41
  • 1
    @iruvar, it's especially important for SIGINT and SIGQUIT as otherwise you'll have troubles if your script is called from a shell that implements that "wait and cooperative exit" approach discussed as that page (which I've amended slightly), but it's a good idea to do it as well for other signals if only to report to your parent that you have been killed. Commented Nov 11, 2018 at 22:01
  • 1
    @HaroldFischer, that resets the EXIT trap which avoids cleanup being run twice upon signal delivery for shells like mksh or ksh93. Commented Mar 29, 2019 at 8:41
1

Warning: exit is not guaranteed to work, you should instead use EXIT

Given that the POSIX standard does not define whether the EXIT trap should also be executed in case of a signal and given the fact that the Bourne Shell does not call the EXIT trap in the case you mention, it is obvious that you are entering unspecified behavior.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .