"When do bash signal handlers NOT work?"
March 19th, 2019
Just in case somebody else runs into this frustrating situation, (at least on RHEL 7) bash shell scripts trying to catch SIGINT do not work when the signal is sent to the process from another pid and your bash script is running a non-shell built-in.
Yesterday I was attempting to mock out a linux command to verify the behavior of a shell script I wrote. The goal was to be able to test the shell script's behavior without actually invoking the particular linux command (see pbench PR #1108). One feature of the script being tested was sending a SIGINT to another process, being sure the script had properly identified the correct process.
There is a nice blog about how to write signal handlers for bash, well worth the read [1]. Using that blog, I came up with a very simple mock command shell script:
#!/bin/bash
printf -- "$0 $@\n"
sighand() {
printf -- "$0: sig handled\n" >&2
exit 0
}
trap sighand SIGINT
sleep 9999
printf -- "$0: done\n"
exit 0
The above script works fine if you run from a shell, and in the same shell you type Ctrl-C. But if you try to send SIGINT from another process, the sleep 9999 is not interrupted.
If instead we place the sleep 9999 in the background and wait for it with the wait built-in, both sending the signal from the same shell via Ctrl-C and from a remote process works just fine:
#!/bin/bash
printf -- "$0 $@\n"
sighand() {
printf -- "$0: sig handled\n" >&2
exit 0
}
trap sighand SIGINT
sleep 9999 &
wait
printf -- "$0: done\n"
exit 0
The reason for this behavior is that the bash parent shell of the command has SIGINT and other signals blocked when executing sleep 9999 which is NOT a built in function of bash. But since wait is a built-in function signals are handled as expected (see the answer to [2] for more information).
[1]: http://linuxcommand.org/lc3_wss0150.php
[2]: https://stackoverflow.com/questions/2524937/how-to-send-a-signal-sigint-from-script-to-script-bash
From Doran:
Good observations, but a couple of points of feedback:
/bin/sleepsubprocess in the background. Best to clean up subprocesses when you exit due to a signal with something like: