Skip to content

Instantly share code, notes, and snippets.

@portante
Last active June 14, 2021 18:10
Show Gist options
  • Save portante/e81bc6b8e7560a6b3d9dd1acfdd4d427 to your computer and use it in GitHub Desktop.
Save portante/e81bc6b8e7560a6b3d9dd1acfdd4d427 to your computer and use it in GitHub Desktop.
Poor Man's Blog: "When do bash signal handlers NOT work?"

"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

@portante
Copy link
Author

From Doran:

Good observations, but a couple of points of feedback:

  1. The signal handler will not be invoked while sh is waiting for the (foreground) subprocess, but it will be invoked once the subprocess exits
  • Compare:
#!/bin/bash

sighand() {
    echo "$(date +%S): sig handled" >&2
    exit 0
}

trap sighand SIGINT

( sleep 1; kill -INT $$ ) &

echo "$(date +%S): sleeping"
sleep 2
echo "$(date +%S): sleep done"
exit 0
  • Output:
47: sleeping
49: sig handled
  1. Your example script correctly handles the signal and exits, but it leaves the /bin/sleep subprocess in the background. Best to clean up subprocesses when you exit due to a signal with something like:
kill $(jobs -p)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment