765 Stimmen

Wie kann man in der Bash warten, bis mehrere Unterprozesse beendet sind, und den Exit-Code !=0 zurückgeben, wenn ein Unterprozess mit dem Code !=0 endet?

Wie kann man in einem Bash-Skript warten, bis mehrere von diesem Skript erzeugte Unterprozesse beendet sind, und dann einen Exit-Code zurückgeben !=0 wenn einer der Unterprozesse mit Code endet !=0 ?

Einfaches Skript:

#!/bin/bash
for i in `seq 0 9`; do
  doCalculations $i &
done
wait

Das obige Skript wartet auf alle 10 gespawnten Unterprozesse, aber es gibt immer den Exit-Status 0 (siehe help wait ). Wie kann ich dieses Skript so ändern, dass es den Exit-Status der gespawnten Unterprozesse erkennt und den Exit-Code 1 wenn einer der Unterprozesse mit Code endet !=0 ?

Gibt es dafür eine bessere Lösung als das Sammeln der PIDs der Unterprozesse, das Warten auf sie in der Reihenfolge und die Summe der Exit-Status?

2 Stimmen

Dies könnte erheblich verbessert werden, um auf folgende Punkte einzugehen wait -n die in der modernen Bash verfügbar ist, um nur zurückzukehren, wenn der erste/nächste Befehl abgeschlossen ist.

0 Stimmen

Wenn Sie mit der Bash testen wollen, versuchen Sie dies: github.com/sstephenson/bats

3 Stimmen

Die aktive Entwicklung von BATS hat sich auf github.com/bats-core/bats-core

1voto

mug896 Punkte 1485

Das Abfangen des CHLD-Signals funktioniert möglicherweise nicht, da einige Signale verloren gehen können, wenn sie gleichzeitig eintreffen.

#!/bin/bash

trap 'rm -f $tmpfile' EXIT

tmpfile=$(mktemp)

doCalculations() {
    echo start job $i...
    sleep $((RANDOM % 5)) 
    echo ...end job $i
    exit $((RANDOM % 10))
}

number_of_jobs=10

for i in $( seq 1 $number_of_jobs )
do
    ( trap "echo job$i : exit value : \$? >> $tmpfile" EXIT; doCalculations ) &
done

wait 

i=0
while read res; do
    echo "$res"
    let i++
done < "$tmpfile"

echo $i jobs done !!!

0voto

Mark Punkte 3451

Ich denke, die einfachste Möglichkeit, Aufträge parallel laufen zu lassen und den Status zu überprüfen, ist die Verwendung temporärer Dateien. Es gibt bereits ein paar ähnliche Antworten (z.B. Nietzche-jou und mug896).

#!/bin/bash
rm -f fail
for i in `seq 0 9`; do
  doCalculations $i || touch fail &
done
wait 
! [ -f fail ]

Der obige Code ist nicht thread-sicher. Wenn Sie befürchten, dass der obige Code zur gleichen Zeit wie Sie selbst ausgeführt wird, sollten Sie einen eindeutigeren Dateinamen verwenden, z. B. fail.$$. Die letzte Zeile dient dazu, die Anforderung zu erfüllen: "return exit code 1 when any of subprocesses ends with code !=0?" Ich habe hier eine zusätzliche Anforderung eingefügt, um aufzuräumen. Es wäre vielleicht klarer gewesen, es so zu schreiben:

#!/bin/bash
trap 'rm -f fail.$$' EXIT
for i in `seq 0 9`; do
  doCalculations $i || touch fail.$$ &
done
wait 
! [ -f fail.$$ ] 

Hier ist ein ähnliches Snippet zum Sammeln von Ergebnissen aus mehreren Aufträgen: Ich erstelle ein temporäres Verzeichnis, speichere die Ergebnisse aller Unteraufgaben in einer separaten Datei und lege sie dann zur Überprüfung ab. Dies entspricht nicht wirklich der Frage - ich füge es als Bonus hinzu:

#!/bin/bash
trap 'rm -fr $WORK' EXIT

WORK=/tmp/$$.work
mkdir -p $WORK
cd $WORK

for i in `seq 0 9`; do
  doCalculations $i >$i.result &
done
wait 
grep $ *  # display the results with filenames and contents

0voto

Mark Punkte 86

Ich hatte eine ähnliche Situation, hatte aber alle möglichen Probleme mit Schleifen-Subshells, die dafür sorgten, dass die anderen Lösungen hier nicht funktionierten, also ließ ich meine Schleife das Skript schreiben, das ich ausführen würde, mit wait am Ende. Effektiv:

#!/bin/bash
echo > tmpscript.sh
for i in `seq 0 9`; do
    echo "doCalculations $i &" >> tmpscript.sh
done
echo "wait" >> tmpscript.sh
chmod u+x tmpscript.sh
./tmpscript.sh

dumm, aber einfach und half bei der anschließenden Fehlersuche.

Hätte ich Zeit gehabt, hätte ich mich eingehender mit GNU beschäftigt. parallel aber es war schwierig mit meinem eigenen "doCalculations"-Prozess.

-1voto

Nietzche-jou Punkte 14185

Ich denke, vielleicht laufen doCalculations; echo "$?" >>/tmp/acc in einem Unterschale die an den Hintergrund gesendet wird, dann wird die wait dann /tmp/acc würde die Exit-Status enthalten, einen pro Zeile. Ich weiß allerdings nicht, welche Folgen das Anhängen mehrerer Prozesse an die Akkumulatordatei hat.

Hier ist ein Versuch zu diesem Vorschlag:

Datei: doCalcualtions

#!/bin/sh

random -e 20
sleep $?
random -e 10

Datei: try

#!/bin/sh

rm /tmp/acc

for i in $( seq 0 20 ) 
do
        ( ./doCalculations "$i"; echo "$?" >>/tmp/acc ) &
done

wait

cat /tmp/acc | fmt
rm /tmp/acc

Ausgabe der laufenden ./try

5 1 9 6 8 1 2 0 9 6 5 9 6 0 0 4 9 5 5 9 8

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X