I have a Bash script that runs a long running process in the foreground. When it receives a SIGQUIT signal, it should perform various cleanup operations such as killing itself and all of its child processes (via kill of process group etc.). A minimal script, that should catch the signal, is shown below (called test_trap.sh
):
#!/bin/bash
trap 'echo "TRAP CAUGHT"; exit 1' QUIT # other required signals are omitted for brevity
echo starting sleep
sleep 11666
echo ending sleep
echo done
I would like to send the SIGHUP signal to the process of the test_trap.sh
script. However, sending a SIGHUP to the test_trap.sh
does not trigger the trap expression, but only when I send the signal to the child sleep 11666
process does the trap fire. Below is a bash session demonstrating this:
bash-4.1$ test_trap.sh &
[1] 19633
bash-4.1$ starting sleep
bash-4.1$ kill -s SIGQUIT 19633
bash-4.1$ jobs
[1]+ Running test_trap.sh &
bash-4.1$ ps -ef --forest --cols=10000 | grep '11666\|test_trap.sh' | grep -v grep
theuser 19633 12227 0 07:40 pts/4 00:00:00 \_ /bin/bash ./test_trap.sh
theuser 19634 19633 0 07:40 pts/4 00:00:00 | \_ sleep 11666
bash-4.1$ kill -s SIGQUIT 19634
bash-4.1$ Quit (core dumped)
TRAP CAUGHT
[1]+ Exit 1 test_trap.sh
bash-4.1$ ps -ef --forest --cols=10000 | grep '11666\|test_trap.sh' | grep -v grep
bash-4.1$
Note that the "sleep 11666" is just a representative process. That process can actually be an interactive subshell (e.g., bash -i
).
Why doesn't the parent test_trap.sh
process catch the SIGHUP signal? Why would the trap fire only when the process for sleep 11666
was signaled?
I do not want to use uncatchable SIGKILL as I do need to do an assortment of cleanup operations in the trap expression.
This script is intended run on any fairly recent version of any Linux distribution containing Bash (e.g., not Cygwin).
References:
bash
must wait for sleep
to complete before it can execute the handler. A good workaround is to run sleep
in the background, then immediately wait for it. While sleep
is uninterruptible, wait
is not.
trap 'kill $sleep_pid; echo "TRAP CAUGHT"; exit 1' QUIT
echo starting sleep
sleep 11666 &
sleep_pid=$!
wait
echo ending sleep
echo done
The recording of sleep_pid
and using it to kill sleep
from the handler are optional.
Actually, bash is receiving the signal, but it is in an uninterruptible state waiting for the sleep
command to end. When it ends, bash will react to the signal and execute the trap.
You can replace the long sleep
command with a loop of short sleep
commands:
while true
do
sleep 1
done
With that, if you send the signal to the bash process, it will react as soon as the currently executing sleep
command ends, that is, at most 1 second after it was sent.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With