Question: I have a bash script process which at some point executes a long-running subprocess synchronously.
During the run of that subprocess, a signal is sent directly to the bash script process requesting the script to terminate.
Is there any way to intercept that signal, terminate the subprocess and then exit the bash process?

Apparently, bash's signal handling never interrupts synchronous calls?

I cannot control the fact that the termination signal is sent to the bash process. Although if the signal could propagate to the child process, that would also solve my issue.

thanks in advance,
Broes

Answer: 
See the man page of bash, chapter **SIGNALS**:

If bash is waiting for a command to complete and receives a signal for which a trap has been set, **the trap will not be executed until the command completes**. When bash is waiting for an asynchronous command via the wait builtin, the reception of a signal for which a trap has been set will cause the wait builtin to return immediately with an exit status greater than 128, immediately after which the trap is executed.

So, run your external program asynchronously and use wait. Kill it using $!.
Here's a bash utility function I wrote to handle this. It's proved useful and robust. I hope you find it useful.

```
# Run a command in a way that can be interrupted by a signal (eg SIGTERM)
#
# When bash receives a SIGTERM it normally simply exits. If it's executing a subprocess
# that subprocess isn't signaled. (Typically that's not a problem for interactive shells
# because the entire Process Group gets sent the signal.)
#
# When running a script it's sometimes useful for the script to propagate a SIGTERM
# to the command that was running. We can do that by using the trap builtin to catch
# the signal. But it's a little tricky, per the bash manual:
#
#    If bash is waiting for a command to complete and receives a signal for
#    which a trap has been set, the trap will not be executed until the
#    command completes.
#
# so a script executing a long-running command with a signal trap set won't
# notice the signal until later. There's a way around that though...
#
#    When bash is waiting for an asynchronous command via the wait builtin, the
#    reception of a signal for which a trap has been set will cause the wait
#    builtin to return immediately with an exit status greater than 128,
#    immediately after which the trap is executed.
#
# Usage:
#
#   interruptable [options] command [args]
#
# Options:
#   --killall - put the child into a process group (via setsid)
#               and send the SIGTERM to the process group
#   --debug   - print a message including pid of the child
#
# Usage examples:
#
#   interruptable sleep 3600
#
# If not interrupted, the exit status of the specified command is returned.
# If interrupted, the specified command is sent a SIGTERM and the current
# shell exits with a status of 143.

interruptable() {

    # handle options
    local setsid=""
    local debug=false
    while true; do
        case "${1:-}" in
            --killall)      setsid=setsid; shift ;;
            --debug)        debug=true; shift ;;
            --*)            echo "Invalid option: $1" 1>&2; exit 1;;
            *)              break;; # no more options
        esac
    done

    # start the specified command
    $setsid "$@" &
    local child_pid=$!

    # arrange to propagate a signal to the child process
    trap '
        exec 1>&2
        set +e
        trap "" SIGPIPE # ensure a possible sigpipe from the echo does not prevent the kill
        echo "${BASH_SOURCE[0]} caught SIGTERM while executing $* (pid $child_pid), sending SIGTERM to it"
        # (race) child may have exited in which case kill will report an error
        # if setsid is used then prefix the pid with a "-" to indicate that the signal
        # should be sent to the entire process group
        kill ${setsid:+-}$child_pid
        exit 143
    ' SIGTERM
    # ensure that the trap doesn't persist after we return
    trap 'trap - SIGTERM' RETURN

    $debug && echo "interruptable wait (child $child_pid, self $$) for: $*"

    # An error status from the child process will trigger an exception (via set -e)
    # here unless the caller is checking the return status
    wait $child_pid # last command, so status of waited for command is returned
}

```