Linux – Pressing Ctrl-C stops bash script, but don’t stop PHP script called by the script

bashlinuxprocess

I need to run a few scripts every few minutes. The logic was made in PHP, and it's working great. To keep things together I've made the bash script below, which also runs fine.

#!/bin/bash

calculaDiff() {
    DIFF=0

    while [ "$DIFF" -eq "0" ]; do
        DIFF=`php calculaDiff.php`
    done;
}

# need to calculate pending diffs
calculaDiff

# main loop
while true; do
    CAPTURA=`php capturaRelatorio.php`
    if [ "$CAPTURA" -eq "0" ]; then
        calculaDiff
    fi

    VERIFICA=`php verificaLimites.php`

done

The script capturaRelatorio.php has a sleep inside it because I can only process it every N minutes. It will print a message saying that it's sleeping for S seconds so I can monitor it.

If I call the bash script and press Ctrl+C at this time, while it's sleeping, it kills the bash script, but doesn't kill the called php script. I understand that there's a different process running it.

So, is there a way to kill the bash script and every "child"? Or should I go with another approach to run these scripts?

Best Answer

From this answer: bash - How to kill all subprocesses of shell? - Stack Overflow.

If you only care about killing direct children, you should be able to do

pkill -P $$

-P says

-P, --parent ppid,...
      Only match processes whose parent process ID is listed.

And $$ means the PID of the current process.

If you need to kill child processes and any processes they may start (grandchildren, and so on), you should be able to use the function that is in a different answer to that question:

kill_descendant_processes() {
    local pid="$1"
    local and_self="${2:-false}"
    if children="$(pgrep -P "$pid")"; then
        for child in $children; do
            kill_descendant_processes "$child" true
        done
    fi
    if [[ "$and_self" == true ]]; then
        kill "$pid"
    fi
}

Like this

kill_descendant_processes $$ true

Which will kill the current process and all descendants. You will probably want to call this from a trap handler. That is, when you press ctrl+c, your script will be sent SIGINT and you can catch that signal and handle it. For example:

trap cleanup INT

cleanup() {
    kill_descendant_processes $$ true
}