I am writing a shell script which performs a task periodically and on receiving a USR1 signal from another process.
The structure of the script is similar to this answer:
#!/bin/bash
trap 'echo "doing some work"' SIGUSR1
while :
do
sleep 10 && echo "doing some work" &
wait $!
done
However, this script has the problem that the sleep process continues in the background and only dies on its timeout. (note that when USR1 is received during wait $!, the sleep process lingers for its regular timeout, but the periodic echo indeed gets cancelled.) You can for example see the number of sleep processes on your machine using pkill -0 -c sleep
.
I read this page, which suggests killing the lingering sleep in the trap action, e.g.
#!/bin/bash
pid=
trap '[[ $pid ]] && kill $pid; echo "doing some work"' SIGUSR1
while :
do
sleep 10 && echo "doing some work" &
pid=$!
wait $pid
pid=
done
However this script has a race condition if we spam our USR1 signal fast e.g. with:
pkill -USR1 trap-test.sh; pkill -USR1 trap-test.sh
then it will try to kill a PID which was already killed and print an error. Not to mention, I do not like this code.
Is there a better way to reliably kill the forked process when interrupted? Or an alternative structure to achieve the same functionality?