Bash: Alle Childs beim Erreichen eines Timeouts toeten

Hin und wieder kann es vorkommen, das ein Bash-Script eine oder mehrere Subshells öffnet, um diverse Jobs im Hintergrund zu erledigen. Dabei kann es aber in vielen Fällen vorkommen, dass eine der Subshells in einen Blocking-State verfällt und hängen bleibt. Das ist vor allem dann verhängnisvoll, wenn das Script automatisiert von beispielsweise CRON ausgeführt wird.

In solchen Szenarien empfiehlt es sich eine Routine zu implementieren, die beim Erreichen eines definierten Timeouts alle Subshells tötet.

Für diesen Fall bietet sich das Linux-Signal SIGALRM an. Wenn das Haupt-Script ein SIGALRM erhält, soll es alle seine Childs töten und sich mit ungleich Null beenden. So muss zusätzlich zu den Workern einfach nur eine weitere Subshell mit GNU/sleep + GNU/kill installiert werden, die nach Ablauf des Timeouts das Signal an den Hauptprozess sendet.

Hier ein Code-Schnipsel, der die Lösung erklärt:

#!/bin/bash

alarmtime=3
childs=3
parentpid=$$
childpids=()

exit_timeout() {
    echo "Alarm signal received : killing all children"
    for child in ${childpids[@]}; do
        kill -STOP $childpid &>/dev/null
    done
    exit 1
}

trap exit_timeout SIGALRM

for i in $(seq $childs); do
    sleep 4 &  # very long task that may timeout
    childpids+=($!)
done

(sleep $alarmtime; kill -ALRM $parentpid) &
alarmpid=$!

wait ${childpids[@]}
echo "Alarm never reached, children exited cleanly."
kill $alarmpid

Ich freue mich, wenn es jemandem hilft. =)