11

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
Kusalananda
355k42 gold badges735 silver badges1.1k bronze badges
asked May 20, 2019 at 21:05
1
  • Is there still something that does not work by Kusalananda's answer? Commented Feb 10, 2020 at 12:40

3 Answers 3

11

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.

answered May 20, 2019 at 21:34
3
  • about exit in a SIGINT trap: unix.stackexchange.com/search?q=wait+and+cooperative+exit Commented 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. Commented 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 a POLL signal. Commented Apr 24, 2021 at 12:44
3

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.

answered May 17, 2022 at 11:33
1

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 ;-\

answered May 20, 2019 at 21:57

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.