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

9voto

Jason Slobotski Punkte 1190

Ich sehe viele gute Beispiele, die hier aufgelistet sind, und wollte auch meins einbringen.

#! /bin/bash

items="1 2 3 4 5 6"
pids=""

for item in $items; do
    sleep $item &
    pids+="$! "
done

for pid in $pids; do
    wait $pid
    if [ $? -eq 0 ]; then
        echo "SUCCESS - Job $pid exited with a status of $?"
    else
        echo "FAILED - Job $pid exited with a status of $?"
    fi
done

Ich verwende etwas sehr ähnliches, um Server/Dienste parallel zu starten/stoppen und jeden Exit-Status zu überprüfen. Funktioniert bei mir hervorragend. Ich hoffe, das hilft jemandem weiter!

8voto

Alnitak Punkte 324207

Ich glaube nicht, dass dies mit den eingebauten Funktionen der Bash möglich ist.

Usted kann eine Benachrichtigung erhalten, wenn ein Kind aussteigt:

#!/bin/sh
set -o monitor        # enable script job control
trap 'echo "child died"' CHLD

Es gibt jedoch keine offensichtliche Möglichkeit, den Exit-Status des Childs im Signalhandler abzurufen.

Die Erlangung des Kinderstatus ist in der Regel Aufgabe des wait Familie von Funktionen in den POSIX-APIs der unteren Ebene. Leider ist die Unterstützung der Bash dafür begrenzt - Sie können auf eine bestimmten Kindprozess (und seinen Exit-Status abfragen) oder Sie können warten, bis tous von ihnen und erhalten immer ein 0-Ergebnis.

Was unmöglich erscheint, ist das Äquivalent zu waitpid(-1) die blockiert, bis tout Kindprozess zurückkehrt.

8voto

errr Punkte 91

Der folgende Code wartet auf den Abschluss aller Berechnungen und gibt den Exit-Status 1 zurück, wenn eine der doCalculations scheitert.

#!/bin/bash
for i in $(seq 0 9); do
   (doCalculations $i >&2 & wait %1; echo $?) &
done | grep -qv 0 && exit 1

7voto

michaelt Punkte 81

Ich habe dies ausprobiert und die besten Teile aus den anderen Beispielen hier kombiniert. Dieses Skript führt die checkpids Funktion, wenn tout Hintergrundprozess beendet, und geben Sie den Exit-Status aus, ohne auf Polling zurückzugreifen.

#!/bin/bash

set -o monitor

sleep 2 &
sleep 4 && exit 1 &
sleep 6 &

pids=`jobs -p`

checkpids() {
    for pid in $pids; do
        if kill -0 $pid 2>/dev/null; then
            echo $pid is still alive.
        elif wait $pid; then
            echo $pid exited with zero exit status.
        else
            echo $pid exited with non-zero exit status.
        fi
    done
    echo
}

trap checkpids CHLD

wait

6voto

stefanct Punkte 2178

Wenn Sie bash 4.2 oder höher zur Verfügung haben, könnte das Folgende für Sie nützlich sein. Es verwendet assoziative Arrays, um Aufgabennamen und ihren "Code" sowie Aufgabennamen und ihre Pids zu speichern. Ich habe auch eine einfache Methode zur Ratenbegrenzung eingebaut, die nützlich sein könnte, wenn Ihre Aufgaben viel CPU- oder E/A-Zeit verbrauchen und Sie die Anzahl der gleichzeitigen Aufgaben begrenzen wollen.

Das Skript startet alle Aufgaben in der ersten Schleife und verbraucht die Ergebnisse in der zweiten Schleife.

Das ist für einfache Fälle ein bisschen übertrieben, aber es ermöglicht ziemlich tolle Sachen. Zum Beispiel kann man Fehlermeldungen für jede Aufgabe in einem anderen assoziativen Array speichern und sie ausgeben, nachdem sich alles beruhigt hat.

#! /bin/bash

main () {
    local -A pids=()
    local -A tasks=([task1]="echo 1"
                    [task2]="echo 2"
                    [task3]="echo 3"
                    [task4]="false"
                    [task5]="echo 5"
                    [task6]="false")
    local max_concurrent_tasks=2

    for key in "${!tasks[@]}"; do
        while [ $(jobs 2>&1 | grep -c Running) -ge "$max_concurrent_tasks" ]; do
            sleep 1 # gnu sleep allows floating point here...
        done
        ${tasks[$key]} &
        pids+=(["$key"]="$!")
    done

    errors=0
    for key in "${!tasks[@]}"; do
        pid=${pids[$key]}
        local cur_ret=0
        if [ -z "$pid" ]; then
            echo "No Job ID known for the $key process" # should never happen
            cur_ret=1
        else
            wait $pid
            cur_ret=$?
        fi
        if [ "$cur_ret" -ne 0 ]; then
            errors=$(($errors + 1))
            echo "$key (${tasks[$key]}) failed."
        fi
    done

    return $errors
}

main

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