I have a script I wrote in Bash that I'm trying to modify to be POSIX-compliant. I have managed to get everything working except I cannot get the EXIT trap to trigger when the process is terminated with killall, CTRL+C or closing the terminal - as it did with Bash. Here is my exit trap:
#!/bin/sh
TMP=$(mktemp /tmp/countdown.XXXXX)
trap 'rm -rf $TMP' EXIT
-
Is there still something that does not work by Kusalananda's answer?jarno– jarno2020年02月10日 12:40:05 +00:00Commented Feb 10, 2020 at 12:40
3 Answers 3
In a strictly POSIX shell, the EXIT
trap is evaluated before the shell exits due to executing exit
or due to executing the last command in a script. It is not executed if the shell exits due to a signal.
Would you want to catch Ctrl+C, you would have to trap INT
(the "interrupt" signal). If closing the terminal sends the script a HUP
("hang-up") signal, you would have to trap that too.
trap 'rm -rf "$TMP"; trap - EXIT; exit' EXIT INT HUP
You may also want to trap TERM
, the generic "terminate" signal sent by default by kill
.
The trap above explicitly resets the EXIT
trap so that it's not called again when the script exits due to receiving one of the listed signals.
-
about
exit
in aSIGINT
trap: unix.stackexchange.com/search?q=wait+and+cooperative+exituser313992– user3139922019年05月21日 00:16:29 +00:00Commented May 21, 2019 at 0:16 -
I guess you should trap even more signals, that have terminate as default action, but signal POLL did not work with
dash
.jarno– jarno2021年04月24日 12:28:34 +00:00Commented Apr 24, 2021 at 12:28 -
@jarno The built-in
kill
utility can be used to test what signals the shell understands:dash -c 'kill -l'
. I have personally never heard of aPOLL
signal.2021年04月24日 12:44:06 +00:00Commented Apr 24, 2021 at 12:44
As mentioned by Kusalananda, EXIT
does not trap signals in POSIX. The simplest solution is to add something like this to your code:
trap exit INT HUP TERM
This will catch the signals you want and call your original trap when it reaches the exit statement.
Just remove the temporary file immediately after creating it, and read & write to it via a file descriptor.
Pass it as /dev/fd/[fd]
or /proc/self/fd/[fd]
to commands which expect a path.
Example:
t=$(mktemp); exec 5<>"$t"; rm "$t"
echo some text >&5
exec 5<>/dev/fd/5 # rewind / seek back at the start of the file
cat <&5
cmd1 /dev/fd/5 # cmd1 only takes paths
cmd2 5>&- # don't leak fd 5 to cmd2
You can also open the tempfile as multiple separate fds, as in exec 5<"$t" 6>"$t"
.
The rewind trick is linux-only; I cannot think of any portable way of doing it; ideas are welcome ;-\