I have a Bash script, which looks similar to this:

#!/bin/bash
echo "Doing some initial work....";
/bin/start/main/server --nodaemon

Now if the bash shell running the script receives a SIGTERM signal, it should also send a SIGTERM to the running server (which blocks, so no trap possible). Is that possible?

up vote 71 down vote accepted

Try:

#!/bin/bash 

_term() { 
  echo "Caught SIGTERM signal!" 
  kill -TERM "$child" 2>/dev/null
}

trap _term SIGTERM

echo "Doing some initial work...";
/bin/start/main/server --nodaemon &

child=$! 
wait "$child"

Normally, bash will ignore any signals while a child process is executing. Starting the server with & will background it into the shell's job control system, with $! holding the server's PID (to be used with wait and kill). Calling wait will then wait for the job with the specified PID (the server) to finish, or for any signals to be fired.

When the shell receives SIGTERM (or the server exits independently), the wait call will return (exiting with the server's exit code, or with the signal number + 127 if a signal was received). Afterward, if the shell received SIGTERM, it will call the _term function specified as the SIGTERM trap handler before exiting (in which we do any cleanup and manually propagate the signal to the server process using kill).

  • Looks good! I'll try it and respond when I tested it. – Lorenz Jul 26 '14 at 20:22
  • 6
    But exec replaces the shell with the given program, I am not clear on why the subsequent wait call is then needed? – iruvar Jul 26 '14 at 22:08
  • 4
    I think that 1_CR's point is valid. Either you simply use exec /bin/start/main/server --nodaemon (in which case the shell process is replaced with the server process and you don't need to propagate any signals) or you use /bin/start/main/server --nodaemon &, but then exec is not really meaningful. – Andreas Veithen Nov 13 '14 at 19:39
  • 1
    If you want your shell script to terminate only after child is terminated, then in the _term() function you should wait "$child" again. This might be necessary if you have some other supervising process waiting for the shell script to die before restarting it again, or if you also trapped EXIT to do some cleanup and neeed it to run only after the child process has finished. – LeoRochael May 8 '17 at 22:54
  • 1
    @AlexanderMills Read the other answers. Either you're looking for exec, or you want to set up traps. – Stuart P. Bentley Sep 29 at 16:06

Bash does not forward signals like SIGTERM to processes it is currently waiting on. If you want to end your script by segueing into your server (allowing it to handle signals and anything else, as if you had started the server directly), you should use exec, which will replace the shell with the process being opened:

#!/bin/bash
echo "Doing some initial work....";
exec /bin/start/main/server --nodaemon

If you need to keep the shell around for some reason (ie. you need to do some cleanup after the server terminates), you should use a combination of trap, wait, and kill. See SensorSmith's answer.

Andreas Veithen points out that if you do not need to return from the call (like in the OP's example) simply calling through the exec command is sufficient (@Stuart P. Bentley's answer). Otherwise the "traditional" trap 'kill $CHILDPID' TERM (@cuonglm's answer) is a start, but the wait call actually returns after the trap handler runs which can still be before the child process actually exits. So an "extra" call to wait is advisable (@user1463361's answer).

While this is an improvement it still has a race condition which means that the process may never exit (unless the signaler retries sending the TERM signal). The window of vulnerability is between registering the trap handler and recording the child's PID.

The following eliminates that vulnerability (packaged in functions for reuse).

prep_term()
{
    unset term_child_pid
    unset term_kill_needed
    trap 'handle_term' TERM INT
}

handle_term()
{
    if [ "${term_child_pid}" ]; then
        kill -TERM "${term_child_pid}" 2>/dev/null
    else
        term_kill_needed="yes"
    fi
}

wait_term()
{
    term_child_pid=$!
    if [ "${term_kill_needed}" ]; then
        kill -TERM "${term_child_pid}" 2>/dev/null 
    fi
    wait ${term_child_pid}
    trap - TERM INT
    wait ${term_child_pid}
}

# EXAMPLE USAGE
prep_term
/bin/something &
wait_term

Provided solution doesn't work for me because process was killed before the wait command actually finished. I found that article http://veithen.github.io/2014/11/16/sigterm-propagation.html, the last snippet work good in my case of application started in the OpenShift with custom sh runner. The sh script is required because I need to have an ability to get thread dumps which is impossible in case PID of Java process is 1.

trap 'kill -TERM $PID' TERM INT
$JAVA_EXECUTABLE $JAVA_ARGS &
PID=$!
wait $PID
trap - TERM INT
wait $PID
EXIT_STATUS=$?

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.