Wenn untergeordnete Prozesse fehlschlagen, beenden Sie alle und beenden Sie das Programm

9

In meinem Skript habe ich einen Datensatz in input_aa, input_ab usw. aufgeteilt. Dann führe ich jeweils dasselbe Python-Skript als solches aus:

# Execute program on each split file
for part in input_*; do
        python3 $part &
done
wait

Meine Frage ist zweifach: Wie erkenne ich, dass ein Python-Prozess fehlgeschlagen ist, und wie töte ich, wenn ich erkannt werde, alle erzeugten Kinder und beende das Skript mit einem Fehler?

Widerfahren
quelle

Antworten:

10

Sie können eine Prozessgruppe verwenden:

set -m
(
   for part in input_*; do
     (python3 "$part" || kill 0) &
   done
   wait
)

set -m(und die optionale POSIX-Shell-Funktion, erforderliche Unix-Shell-Funktion) führt Jobs in ihrer eigenen Prozessgruppe aus. In bash, yash, zsh, mksh, , die die Arbeitsplätze der Subshell wo set -maktiviert ist , so dass die äußere (...)und alle die innerhalb der erstellten Prozesse in derselben Prozessgruppe platziert werden.

Für dashund andere ashbasierte Shells funktioniert dies nur im Shell-Prozess der obersten Ebene. Dieser Code funktioniert also nur, wenn er in eine Subshell eingefügt wird.

Das funktioniert in AT & T kshoder der alten SysV / Bourne-Shell überhaupt nicht.

kill 0 sendet ein SIGTERM-Signal an alle Mitglieder der aktuellen Prozessgruppe.

Stéphane Chazelas
quelle
In Bash. Warum ich einen Shebang aufgenommen habe - die erforderliche Shell ist nicht klar. Gute Antwort
Jim Mcnamara
@jimmcnamara, dass die Arbeiten in bash, dash, yash, mksh, zsh. Grundsätzlich jede POSIX-Shell außer AT & T ksh. set -mist in POSIX (unter-) angegeben, jedoch als optionale Funktion.
Stéphane Chazelas
Ich benutze Solaris. / bin / sh fliegt nicht.
Jim Mcnamara
@jimmcnamara, no / bin / sh unter Solaris 10 und früher ist die Bourne-Shell (keine POSIX-Shell) und unter 11 AT & T ksh. Wie gesagt, es funktioniert in Bash, Dash, Yash, Mksh, Zsh.
Stéphane Chazelas
1
@mikeserv, das würde den Prozess auf 1 zurücksetzen, ihn aber nicht aus der Prozessgruppe entfernen. kill 0tötet alle Mitglieder der Prozessgruppe, unabhängig davon, was ihre Eltern sind. Siehe ps -j, um Prozessgruppen-IDs anzuzeigen.
Stéphane Chazelas
3

Dies ist ein Beispiel. SPIELEN Sie zuerst damit, um genau das zu bekommen, was Sie brauchen. Es kann nicht viel brechen wie es ist.

#!/bin/bash
# Example of killing off all children

> killfile
> outfile.err
kill_em()
{
   echo 'killing all children ' > 2
   while read pid
   do
      kill -0 $pid && kill -9 $pid  # if still running kill it
   done < killfile
   exit 1
}

export grandparentpid=$$
trap 'kill_em' 6
for i in 2 2 3 4 5 6 7 8 9 10
do
        ( sleep $i && ls oinkle  >> outfile 2>> outfile.err &
          pid=$!
          echo $pid >> killfile
          wait $!
          [ $? -ne 0 ] && kill -6 $grandparentpid
        ) &
done
wait

Dies ist so eingerichtet, dass es absichtlich fehlschlägt, da ls oinklees (auf meinem Computer) fehlschlägt.

Wenn Sie nach dem Basteln mit dem Starter-Skript das bekommen, was Sie brauchen --- Ändern:

for i in 2 2 3 4 5 6 7 8 9 10

zu:

for part in input_* 

Veränderung:

sleep $i && ls oinkle 

zu:

python3 $part 

Die Umleitungen dienen zum Speichern von Protokollen. Sie können sie nicht wollen.

Jim Mcnamara
quelle
Es ist ein bisschen rassig. Wenn einer der Jobs fehlschlägt, bevor alle anderen gestartet wurden, enthält Ihr killfileJob möglicherweise nicht alle Pids der Jobs, die gestartet wurden.
Stéphane Chazelas
Einige schlechte Praktiken wie: nicht zitierte Variablen, Verwendung von Signalnummern anstelle von Namen, Verwendung von Signal 6 (z. B. ABRT unter Linux amd64) anstelle von USR1 / USR2 als Benutzersignal, [ $? -ne 0 ]...
Stéphane Chazelas