it-swarm.com.de

Wie kann man in Bash warten, bis mehrere Unterprozesse beendet sind und den Beendigungscode! = 0 zurückgibt, wenn ein Teilprozess mit dem Code! = 0 endet?

Wie kann man in einem Bash-Skript auf mehrere Unterprozesse warten, die aus diesem Skript hervorgehen, um den Exit-Code! = 0 zu beenden und zurückzugeben, wenn einer der Unterprozesse mit Code! = 0 endet?

Einfaches Skript:

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

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

Gibt es eine bessere Lösung dafür, als die PIDs der Unterprozesse zu sammeln, in der Reihenfolge darauf zu warten und die Exit-Status zu summieren?

458
tkokoszka

wait auch (optional) nimmt die PID des Prozesses ab und wartet mit $! Sie erhalten die PID des letzten Befehls, der im Hintergrund gestartet wird. Ändern Sie die Schleife, um die PID jedes erzeugten Unterprozesses in einem Array zu speichern, und warten Sie dann erneut auf jede PID.

# run processes and store pids in array
for i in $n_procs; do
    ./procs[${i}] &
    pids[${i}]=$!
done

# wait for all pids
for pid in ${pids[*]}; do
    wait $pid
done
412
Luca Tettamanti

http://jeremy.zawodny.com/blog/archives/010717.html :

#!/bin/bash

FAIL=0

echo "starting"

./sleeper 2 0 &
./sleeper 2 1 &
./sleeper 3 0 &
./sleeper 2 0 &

for job in `jobs -p`
do
echo $job
    wait $job || let "FAIL+=1"
done

echo $FAIL

if [ "$FAIL" == "0" ];
then
echo "YAY!"
else
echo "FAIL! ($FAIL)"
fi
264
HoverHell

Wenn Sie GNU Parallel installiert haben, können Sie Folgendes tun:

# If doCalculations is a function
export -f doCalculations
seq 0 9 | parallel doCalculations {}

GNU Parallel gibt Ihnen einen Exit-Code:

  • 0 - Alle Jobs liefen fehlerfrei.

  • 1-253 - Einige Aufträge sind fehlgeschlagen. Der Beendigungsstatus gibt die Anzahl der fehlgeschlagenen Jobs an

  • 254 - Mehr als 253 Jobs sind fehlgeschlagen.

  • 255 - Anderer Fehler.

In den Intro-Videos erfahren Sie mehr: http://pi.dk/1

45
Ole Tange

Hier ist was ich mir bis jetzt ausgedacht habe. Ich würde gerne sehen, wie man den Schlafbefehl unterbricht, wenn ein Kind aufhört, so dass man WAITALL_DELAY nicht auf seinen Gebrauch einstellen muss.

waitall() { # PID...
  ## Wait for children to exit and indicate whether all exited with 0 status.
  local errors=0
  while :; do
    debug "Processes remaining: $*"
    for pid in "[email protected]"; do
      shift
      if kill -0 "$pid" 2>/dev/null; then
        debug "$pid is still alive."
        set -- "[email protected]" "$pid"
      Elif wait "$pid"; then
        debug "$pid exited with zero exit status."
      else
        debug "$pid exited with non-zero exit status."
        ((++errors))
      fi
    done
    (("$#" > 0)) || break
    # TODO: how to interrupt this sleep when a child terminates?
    sleep ${WAITALL_DELAY:-1}
   done
  ((errors == 0))
}

debug() { echo "DEBUG: $*" >&2; }

pids=""
for t in 3 5 4; do 
  sleep "$t" &
  pids="$pids $!"
done
waitall $pids
38
Mark Edgar

Wie wäre es einfach:

#!/bin/bash

pids=""

for i in `seq 0 9`; do
   doCalculations $i &
   pids="$pids $!"
done

wait $pids

...code continued here ...

Update:

Wie aus mehreren Kommentaren hervorgeht, wartet das Obige darauf, dass alle Prozesse abgeschlossen sind, bevor es fortfährt, wird jedoch nicht beendet und schlägt fehl, wenn einer von ihnen fehlschlägt. Dies kann mit der folgenden von @Bryan, @SamBrightman und anderen vorgeschlagenen Änderung zusammenhängen : 

#!/bin/bash

pids=""
RESULT=0


for i in `seq 0 9`; do
   doCalculations $i &
   pids="$pids $!"
done

for pid in $pids; do
    wait $pid || let "RESULT=1"
done

if [ "$RESULT" == "1" ];
    then
       exit 1
fi

...code continued here ...
35
patapouf_ai

Hier ist ein einfaches Beispiel mit wait.

Führen Sie einige Prozesse aus:

$ sleep 10 &
$ sleep 10 &
$ sleep 20 &
$ sleep 20 &

Dann warten Sie mit dem Befehl wait darauf:

$ wait < <(jobs -p)

Oder einfach nur wait (ohne Argumente) für alle.

Dies wird warten, bis alle Jobs im Hintergrund abgeschlossen sind.

Wenn die Option -n angegeben ist, wird gewartet, bis der nächste Job beendet wird, und der Exitstatus wird zurückgegeben.

Siehe: help wait und help jobs für die Syntax.

Der Nachteil ist jedoch, dass nur der Status der letzten ID zurückgegeben wird. Daher müssen Sie den Status für jeden Unterprozess überprüfen und in der Variablen speichern.

Oder erstellen Sie Ihre Berechnungsfunktion, um eine Datei im Fehlerfall zu erstellen (leer oder mit Fehlerprotokoll), und prüfen Sie dann, ob diese Datei vorhanden ist, z.

$ sleep 20 && true || tee fail &
$ sleep 20 && false || tee fail &
$ wait < <(jobs -p)
$ test -f fail && echo Calculation failed.
26
kenorb

Um dies zu parallelisieren ...

for i in $(whatever_list) ; do
   do_something $i
done

Übersetzen Sie es zu diesem ...

for i in $(whatever_list) ; do echo $i ; done | ## execute in parallel...
   (
   export -f do_something ## export functions (if needed)
   export PATH ## export any variables that are required
   xargs -I{} --max-procs 0 bash -c ' ## process in batches...
      {
      echo "processing {}" ## optional
      do_something {}
      }' 
   )
  • Wenn ein Fehler in einem Prozess auftritt, werden die anderen Prozesse nicht unterbrochen, aber führt zu einem Exit-Code ungleich Null aus der gesamten Sequenz.
  • Das Exportieren von Funktionen und Variablen kann in bestimmten Fällen erforderlich sein oder nicht.
  • Sie können --max-procs basierend auf der gewünschten Parallelität einstellen (0 bedeutet "alle gleichzeitig").
  • GNU Parallel bietet einige zusätzliche Funktionen, wenn sie anstelle von xargs verwendet wird - es ist jedoch nicht immer standardmäßig installiert.
  • Die for-Schleife ist in diesem Beispiel nicht unbedingt erforderlich, da echo $i im Grunde nur die Ausgabe von $(whatever_list neu generiert. Ich denke nur, dass die Verwendung des Schlüsselworts for es etwas einfacher macht, zu sehen, was los ist.
  • Die Handhabung von Bash-Strings kann verwirrend sein - ich habe festgestellt, dass die Verwendung von einfachen Anführungszeichen am besten für das Wrappen von nicht trivialen Skripts geeignet ist.
  • Sie können die gesamte Operation leicht unterbrechen (mit ^ C oder ähnlichem), im Gegensatz zum direkteren Ansatz der Bash-Parallelität .

Hier ist ein vereinfachtes Arbeitsbeispiel ...

for i in {0..5} ; do echo $i ; done |xargs -I{} --max-procs 2 bash -c '
   {
   echo sleep {}
   sleep 2s
   }'
17
nobar

Ich glaube nicht, dass es mit der eingebauten Funktionalität von Bash möglich ist.

Sie können erhalten eine Benachrichtigung, wenn ein Kind das Programm verlässt:

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

Es gibt jedoch keine offensichtliche Möglichkeit, den Ausgangsstatus des Kindes im Signalhandler abzurufen.

Normalerweise ist es Aufgabe der wait -Funktionsfamilie in den POSIX-APIs der unteren Ebene, diesen untergeordneten Status zu erhalten. Leider ist die Unterstützung von Bash dafür begrenzt - Sie können auf einen bestimmten untergeordneten Prozess warten (und dessen Beendigungsstatus erhalten) oder Sie können auf alle warten von ihnen und erhalten immer ein 0-Ergebnis.

Was unmöglich erscheint, ist das Äquivalent von waitpid(-1), das blockiert, bis ein untergeordneter Prozess zurückkehrt.

7
Alnitak

Ich sehe hier viele gute Beispiele, wollte auch meine einwerfen.

#! /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 benutze etwas sehr ähnliches, um Server/Dienste parallel zu starten/stoppen und jeden Exit-Status zu überprüfen. Funktioniert gut für mich. Hoffe das hilft jemandem raus!

7
Jason Slobotski

Der folgende Code wartet auf den Abschluss aller Berechnungen und gibt den Exit-Status 1 zurück, wenn doCalculations fehlschlägt.

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

Hier ist meine Version, die für mehrere Pids funktioniert, protokolliert Warnungen, wenn die Ausführung zu lange dauert, und stoppt die Unterprozesse, wenn die Ausführung länger als ein bestimmter Wert dauert.

function WaitForTaskCompletion {
    local pids="${1}" # pids to wait for, separated by semi-colon
    local soft_max_time="${2}" # If execution takes longer than $soft_max_time seconds, will log a warning, unless $soft_max_time equals 0.
    local hard_max_time="${3}" # If execution takes longer than $hard_max_time seconds, will stop execution, unless $hard_max_time equals 0.
    local caller_name="${4}" # Who called this function
    local exit_on_error="${5:-false}" # Should the function exit program on subprocess errors       

    Logger "${FUNCNAME[0]} called by [$caller_name]."

    local soft_alert=0 # Does a soft alert need to be triggered, if yes, send an alert once 
    local log_ttime=0 # local time instance for comparaison

    local seconds_begin=$SECONDS # Seconds since the beginning of the script
    local exec_time=0 # Seconds since the beginning of this function

    local retval=0 # return value of monitored pid process
    local errorcount=0 # Number of pids that finished with errors

    local pidCount # number of given pids

    IFS=';' read -a pidsArray <<< "$pids"
    pidCount=${#pidsArray[@]}

    while [ ${#pidsArray[@]} -gt 0 ]; do
        newPidsArray=()
        for pid in "${pidsArray[@]}"; do
            if kill -0 $pid > /dev/null 2>&1; then
                newPidsArray+=($pid)
            else
                wait $pid
                result=$?
                if [ $result -ne 0 ]; then
                    errorcount=$((errorcount+1))
                    Logger "${FUNCNAME[0]} called by [$caller_name] finished monitoring [$pid] with exitcode [$result]."
                fi
            fi
        done

        ## Log a standby message every hour
        exec_time=$(($SECONDS - $seconds_begin))
        if [ $((($exec_time + 1) % 3600)) -eq 0 ]; then
            if [ $log_ttime -ne $exec_time ]; then
                log_ttime=$exec_time
                Logger "Current tasks still running with pids [${pidsArray[@]}]."
            fi
        fi

        if [ $exec_time -gt $soft_max_time ]; then
            if [ $soft_alert -eq 0 ] && [ $soft_max_time -ne 0 ]; then
                Logger "Max soft execution time exceeded for task [$caller_name] with pids [${pidsArray[@]}]."
                soft_alert=1
                SendAlert

            fi
            if [ $exec_time -gt $hard_max_time ] && [ $hard_max_time -ne 0 ]; then
                Logger "Max hard execution time exceeded for task [$caller_name] with pids [${pidsArray[@]}]. Stopping task execution."
                kill -SIGTERM $pid
                if [ $? == 0 ]; then
                    Logger "Task stopped successfully"
                else
                    errrorcount=$((errorcount+1))
                fi
            fi
        fi

        pidsArray=("${newPidsArray[@]}")
        sleep 1
    done

    Logger "${FUNCNAME[0]} ended for [$caller_name] using [$pidCount] subprocesses with [$errorcount] errors."
    if [ $exit_on_error == true ] && [ $errorcount -gt 0 ]; then
        Logger "Stopping execution."
        exit 1337
    else
        return $errorcount
    fi
}

# Just a plain stupid logging function to replace with yours
function Logger {
    local value="${1}"

    echo $value
}

Warten Sie, bis alle drei Prozesse abgeschlossen sind, und protokollieren Sie eine Warnung, wenn für die Ausführung des Protokollierers mehr als 5 Sekunden erforderlich sind. Beenden Sie das Programm nicht bei Fehlern.

function something {

    sleep 10 &
    pids="$!"
    sleep 12 &
    pids="$pids;$!"
    sleep 9 &
    pids="$pids;$!"

    WaitForTaskCompletion $pids 5 120 ${FUNCNAME[0]} false
}
# Launch the function
someting
5
Orsiris de Jong

Speichern Sie einfach die Ergebnisse aus der Shell, z. in einer Datei.

#!/bin/bash
tmp=/tmp/results

: > $tmp  #clean the file

for i in `seq 0 9`; do
  (doCalculations $i; echo $i:$?>>$tmp)&
done      #iterate

wait      #wait until all ready

sort $tmp | grep -v ':0'  #... handle as required
5
estani

Wenn Sie über Bash 4.2 oder höher verfügen, können die folgenden Informationen für Sie nützlich sein. Es verwendet assoziative Arrays zum Speichern von Tasknamen und deren "Code" sowie Tasknamen und deren PIDs. Ich habe auch eine einfache ratenbegrenzende Methode eingebaut, die nützlich sein könnte, wenn Ihre Aufgaben viel CPU- oder E/A-Zeit beanspruchen und Sie die Anzahl der gleichzeitigen Aufgaben begrenzen möchten.

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

Dies ist ein wenig übertrieben für einfache Fälle, aber es erlaubt ziemlich ordentlich Sachen. Zum Beispiel kann man Fehlermeldungen für jede Aufgabe in einem anderen assoziativen Array speichern und drucken, 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
4
stefanct

Ich habe gerade ein Skript modifiziert, um einen Prozess im Hintergrund zu parallelisieren.

Ich habe ein wenig experimentiert (unter Solaris sowohl mit bash als auch mit ksh) und habe festgestellt, dass "wait" den Exit-Status ausgibt, wenn es nicht Null ist, oder eine Liste von Jobs, die einen Exit ungleich Null zurückgeben, wenn kein PID-Argument angegeben wird. Z.B.

Bash:

$ sleep 20 && exit 1 &
$ sleep 10 && exit 2 &
$ wait
[1]-  Exit 2                  sleep 20 && exit 2
[2]+  Exit 1                  sleep 10 && exit 1

Ksh:

$ sleep 20 && exit 1 &
$ sleep 10 && exit 2 &
$ wait
[1]+  Done(2)                  sleep 20 && exit 2
[2]+  Done(1)                  sleep 10 && exit 1

Diese Ausgabe wird in stderr geschrieben. Eine einfache Lösung für das OP-Beispiel könnte beispielsweise lauten:

#!/bin/bash

trap "rm -f /tmp/x.$$" EXIT

for i in `seq 0 9`; do
  doCalculations $i &
done

wait 2> /tmp/x.$$
if [ `wc -l /tmp/x.$$` -gt 0 ] ; then
  exit 1
fi

Während dies:

wait 2> >(wc -l)

gibt auch eine Zählung zurück, jedoch ohne die tmp-Datei. Dies kann beispielsweise auch folgendermaßen verwendet werden:

wait 2> >(if [ `wc -l` -gt 0 ] ; then echo "ERROR"; fi)

Dies ist jedoch nicht viel nützlicher als die tmp-Datei IMO. Ich konnte keinen nützlichen Weg finden, die tmp-Datei zu vermeiden, und auch das "wait" in einer Subshell auszuführen, die überhaupt nicht funktioniert.

4
Tosh
#!/bin/bash
set -m
for i in `seq 0 9`; do
  doCalculations $i &
done
while fg; do true; done
  • Mit set -m können Sie fg & bg in einem Skript verwenden
  • Die Variable fg hat nicht nur den letzten Prozess in den Vordergrund, sondern hat auch denselben Exit-Status wie der Prozess, den er im Vordergrund hat
  • while fg stoppt die Schleife, wenn eine fg mit einem Exit-Status ungleich Null beendet wird

unglücklicherweise behandelt dies den Fall nicht, wenn ein Prozess im Hintergrund mit einem Exit-Status ungleich Null beendet wird. (Die Schleife wird nicht sofort beendet. Sie wartet, bis die vorherigen Prozesse abgeschlossen sind.)

3
Jayen

Dies funktioniert, sollte genauso gut, wenn nicht besser als die Antwort von @ HoverHell sein!

#!/usr/bin/env bash

set -m # allow for job control
EXIT_CODE=0;  # exit code of overall script

function foo() {
     echo "CHLD exit code is $1"
     echo "CHLD pid is $2"
     echo $(jobs -l)

     for job in `jobs -p`; do
         echo "PID => ${job}"
         wait ${job} ||  echo "At least one test failed with exit code => $?" ; EXIT_CODE=1
     done
}

trap 'foo $? $$' CHLD

DIRN=$(dirname "$0");

commands=(
    "{ echo "foo" && exit 4; }"
    "{ echo "bar" && exit 3; }"
    "{ echo "baz" && exit 5; }"
)

clen=`expr "${#commands[@]}" - 1` # get length of commands - 1

for i in `seq 0 "$clen"`; do
    (echo "${commands[$i]}" | bash) &   # run the command via bash in subshell
    echo "$i ith command has been issued as a background job"
done

# wait for all to finish
wait;

echo "EXIT_CODE => $EXIT_CODE"
exit "$EXIT_CODE"

# end

und natürlich habe ich dieses Skript in einem NPM-Projekt unsterblich gemacht, in dem Sie bash-Befehle parallel ausführen können, was zum Testen nützlich ist:

https://github.com/ORESoftware/generic-subshell

3
Alexander Mills

Ich habe es versucht und hier die besten Teile aus den anderen Beispielen zusammengestellt. Dieses Skript führt die Funktion checkpids aus, wenn any background beendet wird, und gibt den Exitstatus aus, ohne auf Abfragen 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
3
michaelt

Es gibt bereits viele Antworten, aber ich bin überrascht, dass niemand vorgeschlagen hat, Arrays zu verwenden.

n=10 # run 10 jobs
c=0
PIDS=()

while true

    my_function_or_command &
    PID=$!
    echo "Launched job as PID=$PID"
    PIDS+=($PID)

    (( c+=1 ))

    # required to prevent any exit due to error
    # caused by additional commands run which you
    # may add when modifying this example
    true

do

    if (( c < n ))
    then
        continue
    else
        break
    fi
done 


# collect launched jobs

for pid in "${PIDS[@]}"
do
    wait $pid || echo "failed job PID=$pid"
done
2
user3728501
set -e
fail () {
    touch .failure
}
expect () {
    wait
    if [ -f .failure ]; then
        rm -f .failure
        exit 1
    fi
}

sleep 2 || fail &
sleep 2 && false || fail &
sleep 2 || fail
expect

Der set -e oben bewirkt, dass Ihr Skript bei einem Fehler angehalten wird.

expect gibt 1 zurück, wenn ein Subjob fehlgeschlagen ist.

2
Yajo

falle ist dein Freund. Sie können in vielen Systemen auf ERR fangen. Sie können EXIT oder DEBUG abfangen, um nach jedem Befehl ein Stück Code auszuführen.

Dies zusätzlich zu allen Standardsignalen.

2
Paul Hodges

Das Überfüllen des CHLD-Signals funktioniert möglicherweise nicht, da Sie möglicherweise Signale verlieren, wenn sie gleichzeitig ankommen.

#!/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 !!!
1
mug896

die Lösung zum Warten auf mehrere Unterprozesse und zum Beenden, wenn einer von ihnen mit einem Statuscode ungleich Null endet, ist 'wait -n'.

#!/bin/bash
wait_for_pids()
{
    for (( i = 1; i <= $#; i++ )) do
        wait -n [email protected]
        status=$?
        echo "received status: "$status
        if [ $status -ne 0 ] && [ $status -ne 127 ]; then
            exit 1
        fi
    done
}

sleep_for_10()
{
    sleep 10
    exit 10
}

sleep_for_20()
{
    sleep 20
}

sleep_for_10 &
pid1=$!

sleep_for_20 &
pid2=$!

wait_for_pids $pid2 $pid1

statuscode '127' ist für einen nicht vorhandenen Prozess, was bedeutet, dass das Kind möglicherweise beendet wurde.

1
user3898515

Ich habe dies kürzlich verwendet (danke Alnitak):

#!/bin/bash
# activate child monitoring
set -o monitor

# locking subprocess
(while true; do sleep 0.001; done) &
pid=$!

# count, and kill when all done
c=0
function kill_on_count() {
    # you could kill on whatever criterion you wish for
    # I just counted to simulate bash's wait with no args
    [ $c -eq 9 ] && kill $pid
    c=$((c+1))
    echo -n '.' # async feedback (but you don't know which one)
}
trap "kill_on_count" CHLD

function save_status() {
    local i=$1;
    local rc=$2;
    # do whatever, and here you know which one stopped
    # but remember, you're called from a subshell
    # so vars have their values at fork time
}

# care must be taken not to spawn more than one child per loop
# e.g don't use `seq 0 9` here!
for i in {0..9}; do
    (doCalculations $i; save_status $i $?) &
done

# wait for locking subprocess to be killed
wait $pid
echo

Von dort kann man leicht extrapolieren und einen Auslöser haben (eine Datei berühren, ein Signal senden) und die Zählkriterien (gezählte Dateien oder was auch immer) ändern, um auf diesen Auslöser zu reagieren. Oder wenn Sie nur "beliebige" Nicht-Null-Rc wollen, beenden Sie einfach die Sperre von save_status.

1
Lloeki

Ich brauchte das, aber der Zielprozess war kein Kind der aktuellen Shell. In diesem Fall funktioniert wait $PID nicht. Ich habe stattdessen folgende Alternative gefunden:

while [ -e /proc/$PID ]; do sleep 0.1 ; done

Dies hängt von der Anwesenheit von procfs ab, die möglicherweise nicht verfügbar ist (Mac gibt es beispielsweise nicht an). Für die Portabilität können Sie stattdessen Folgendes verwenden:

while ps -p $PID >/dev/null ; do sleep 0.1 ; done
1
troelskn

Dies ist etwas, das ich benutze:

#wait for jobs
for job in `jobs -p`; do wait ${job}; done
0
jplozier

Ich denke, der einfachste Weg, Jobs parallel auszuführen und den Status zu prü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 threadsicher. Wenn Sie befürchten, dass der obige Code zur gleichen Zeit wie er selbst ausgeführt wird, sollten Sie einen eindeutigeren Dateinamen verwenden, z. B. "fail". $$. Die letzte Zeile soll die Anforderung erfüllen: "Exit-Code 1 zurückgeben, wenn einer der Unterprozesse mit Code endet! = 0?" Ich habe dort eine zusätzliche Anforderung zum Reinigen geworfen. Es könnte klarer gewesen sein, 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 ähnlicher Ausschnitt zum Erfassen von Ergebnissen aus mehreren Jobs: Ich erstelle ein temporäres Verzeichnis, führe die Ausgaben aller Unteraufgaben in einer separaten Datei aus und lade sie zur Überprüfung ab. Das stimmt nicht wirklich mit der Frage überein - ich werfe es als Bonus:

#!/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
0
Mark

Es kann vorkommen, dass der Prozess abgeschlossen ist, bevor auf den Prozess gewartet wird. Wenn wir auf einen bereits abgeschlossenen Prozess warten, wird ein Fehler ausgelöst, z. B. dass pid kein Kind dieser Shell ist. Um solche Fälle zu vermeiden, kann mit der folgenden Funktion ermittelt werden, ob der Prozess abgeschlossen ist oder nicht:

isProcessComplete(){
PID=$1
while [ -e /proc/$PID ]
do
    echo "Process: $PID is still running"
    sleep 5
done
echo "Process $PID has finished"
}
0
Anju Prasannan