web-dev-qa-db-fra.com

Comment attendre en bash que plusieurs sous-processus se terminent et renvoyer le code de sortie! = 0 lorsqu'un sous-processus se termine avec le code! = 0?

Comment attendre dans un script bash plusieurs sous-processus générés à partir de ce script pour terminer et renvoyer le code de sortie! = 0 lorsque l'un des sous-processus se termine par le code! = 0?

Script simple:

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

Le script ci-dessus attendra les 10 sous-processus générés, mais donnera toujours le statut de sortie 0 (voir help wait). Comment puis-je modifier ce script pour qu'il détecte les statuts de sortie des sous-processus générés et renvoie le code de sortie 1 lorsqu'un des sous-processus se termine par le code! = 0?

Existe-t-il une meilleure solution que de collecter les PID des sous-processus, de les attendre dans l’ordre et de faire la somme des statuts de sortie?

458
tkokoszka

wait aussi (facultatif) prend l'attribut PID du processus à attendre, et avec $! vous obtenez le PID de la dernière commande lancée en arrière-plan . Modifiez la boucle pour stocker le PID de chaque sous-processus généré dans un tableau, puis effectuez une nouvelle boucle d'attente sur chaque 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

Si vous avez GNU Parallel installé, vous pouvez faire:

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

GNU Parallel vous donnera le code de sortie:

  • 0 - Tous les travaux ont été exécutés sans erreur.

  • 1-253 - Certains des travaux ont échoué. Le statut de sortie indique le nombre de tâches ayant échoué.

  • 254 - Plus de 253 emplois ont échoué.

  • 255 - Autre erreur.

Regardez les vidéos d'introduction pour en savoir plus: http://pi.dk/1

45
Ole Tange

Voici ce que j'ai imaginé jusqu'à présent. Je voudrais voir comment interrompre la commande de veille si un enfant se termine, pour ne pas avoir à ajuster WAITALL_DELAY à son utilisation.

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 "$@"; do
      shift
      if kill -0 "$pid" 2>/dev/null; then
        debug "$pid is still alive."
        set -- "$@" "$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

Pourquoi pas simplement:

#!/bin/bash

pids=""

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

wait $pids

...code continued here ...

Mettre à jour:

Comme l'ont souligné de nombreux commentateurs, tous les processus ci-dessus attendent d'être terminés avant de continuer, mais ne s'arrêtent pas et échouent si l'un d'entre eux échoue. Cela peut être fait avec la modification suivante suggérée par @Bryan, @SamBrightman : 

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

Voici un exemple simple utilisant wait.

Exécuter des processus:

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

Puis attendez-les avec la commande wait:

$ wait < <(jobs -p)

Ou simplement wait (sans arguments) pour tous.

Cela attend que tous les travaux en arrière-plan soient terminés.

Si l'option -n est fournie, attend la fin du travail suivant et renvoie son statut de sortie.

Voir: help wait et help jobs pour la syntaxe.

Toutefois, l'inconvénient est que cela ne renvoie que le statut du dernier ID. Vous devez donc vérifier le statut de chaque sous-processus et le stocker dans la variable.

Ou demandez à votre fonction de calcul de créer un fichier en cas d’échec (journal vide ou avec échec), puis vérifiez si ce fichier existe, par exemple.

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

Pour paralléliser cela ...

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

Traduis-le en ceci ...

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 {}
      }' 
   )
  • Si une erreur se produit dans un processus, elle n'interrompra pas les autres processus, mais elle générera un code de sortie non nul de la séquence dans son ensemble.
  • L'exportation de fonctions et de variables peut être nécessaire ou non, dans certains cas.
  • Vous pouvez définir --max-procs en fonction du degré de parallélisme souhaité (0 signifie "tout à la fois").
  • GNU Parallel offre des fonctionnalités supplémentaires lorsqu'il est utilisé à la place de xargs - mais il n'est pas toujours installé par défaut.
  • La boucle for n'est pas strictement nécessaire dans cet exemple car echo $i ne fait que régénérer la sortie de $(whatever_list). Je pense simplement que l’utilisation du mot clé for facilite la compréhension de ce qui se passe.
  • La gestion des chaînes Bash peut être déroutante. J'ai constaté que l'utilisation de guillemets simples était préférable pour le traitement de scripts non triviaux.
  • Vous pouvez facilement interrompre toute l'opération (en utilisant ^ C ou similaire), contrairement à l'approche plus directe du parallélisme de Bash .

Voici un exemple de travail simplifié ...

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

Je ne crois pas que ce soit possible avec la fonctionnalité intégrée de Bash.

Vous pouvez recevoir une notification à la sortie d'un enfant:

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

Cependant, il n'y a aucun moyen apparent d'obtenir le statut de sortie de l'enfant dans le gestionnaire de signaux.

L'obtention de ce statut enfant correspond généralement au travail de la famille wait dans les API POSIX de niveau inférieur. Malheureusement, le support de Bash pour cela est limité - vous pouvez attendre un processus enfant spécifique à un (et obtenir son statut de sortie) ou attendre tous et obtenir toujours le résultat 0.

Ce qui semble impossible est l'équivalent de waitpid(-1), qui bloque jusqu'au retour de tout processus enfant

7
Alnitak

Je vois beaucoup de bons exemples énumérés ici, je voulais aussi ajouter le mien.

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

J'utilise quelque chose de très similaire pour démarrer/arrêter les serveurs/services en parallèle et vérifier l'état de chaque sortie. Ça marche bien pour moi. J'espère que cela aide quelqu'un!

7
Jason Slobotski

Le code suivant attend la fin de tous les calculs et renvoie le statut de sortie 1 si l’un des doCalculations échoue.

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

Voici ma version qui fonctionne pour plusieurs pids, enregistre les avertissements si l'exécution prend trop de temps et arrête les sous-processus si l'exécution prend plus de temps qu'une valeur donnée.

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
}

Par exemple, attendez que les trois processus soient terminés, enregistrez un avertissement si l'exécution prend plus de 5 secondes, arrêtez tous les processus si l'exécution prend plus de 120 secondes. Ne quittez pas le programme en cas d'échec.

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

Stockez simplement les résultats dans Shell, par exemple. dans un fichier.

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

Si vous disposez de bash 4.2 ou version ultérieure, les éléments suivants pourraient vous être utiles. Il utilise des tableaux associatifs pour stocker les noms de tâches et leur "code", ainsi que les noms de tâches et leurs pids. J'ai également intégré une méthode simple de limitation de débit qui peut s'avérer utile si vos tâches consomment beaucoup de temps processeur ou d'E/S et que vous souhaitez limiter le nombre de tâches simultanées.

Le script lance toutes les tâches dans la première boucle et utilise les résultats dans la seconde.

C'est un peu exagéré pour des cas simples, mais cela permet des choses très soignées. Par exemple, il est possible de stocker des messages d'erreur pour chaque tâche dans un autre tableau associatif et de les imprimer une fois que tout est réglé.

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

Je viens de modifier un script en arrière-plan et de paralléliser un processus.

J'ai expérimenté (sous Solaris, bash et ksh) et découvert qu'attendre renvoie l'état de sortie s'il n'est pas nul ou une liste de travaux renvoyant une sortie non nulle lorsque aucun argument PID n'est fourni. Par exemple.

Frapper:

$ 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

Cette sortie est écrite dans stderr. Une solution simple à l'exemple des PO pourrait donc être:

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

Alors que ce:

wait 2> >(wc -l)

renverra également un compte mais sans le fichier tmp. Cela pourrait aussi être utilisé de cette façon, par exemple:

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

Mais ce n’est pas beaucoup plus utile que le fichier tmp IMO. Je ne pouvais pas trouver un moyen utile d'éviter le fichier tmp tout en évitant également d'exécuter "wait" dans un sous-shell, ce qui ne fonctionnerait pas du tout.

4
Tosh
#!/bin/bash
set -m
for i in `seq 0 9`; do
  doCalculations $i &
done
while fg; do true; done
  • set -m vous permet d'utiliser fg & bg dans un script
  • fg, en plus de placer le dernier processus au premier plan, a le même statut de sortie que le processus au premier plan
  • while fg arrêtera la boucle quand une fg quittera avec un statut de sortie différent de zéro

malheureusement, cela ne résoudra pas le problème lorsqu'un processus en arrière-plan se termine avec un statut de sortie non nul. (la boucle ne se terminera pas immédiatement. elle attendra la fin des processus précédents.)

3
Jayen

Cela fonctionne, devrait être tout aussi bon sinon meilleur que la réponse de @ HoverHell!

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

et bien sûr, j'ai immortalisé ce script dans un projet NPM qui vous permet d'exécuter des commandes bash en parallèle, utile pour les tests:

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

3
Alexander Mills

Je me suis essayé à cela et j'ai combiné toutes les meilleures parties des autres exemples ici. Ce script exécute la fonction checkpids lorsque un processus d'arrière-plan se termine et génère l'état de sortie sans recourir à la scrutation. 

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

Il y a déjà beaucoup de réponses ici, mais je suis surpris que personne ne semble avoir suggéré d'utiliser des tableaux ... Alors voici ce que j'ai fait - cela pourrait être utile à certains à l'avenir.

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

Le set -e en haut arrête votre script en cas d'échec.

expect renverra 1 si un sous-travail a échoué.

2
Yajo

le piège est ton ami. Vous pouvez piéger sur ERR dans beaucoup de systèmes. Vous pouvez intercepter EXIT ou sur DEBUG pour exécuter un morceau de code après chaque commande.

Ceci en plus de tous les signaux standard.

2
Paul Hodges

L'interception du signal CHLD peut ne pas fonctionner car vous pourriez perdre certains signaux s'ils arrivaient simultanément.

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

la solution permettant d’attendre plusieurs sous-processus et de se fermer lorsque l’un d’eux se termine avec un code d’état différent de zéro est en utilisant 'wait -n'

#!/bin/bash
wait_for_pids()
{
    for (( i = 1; i <= $#; i++ )) do
        wait -n $@
        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

le code d'état '127' est destiné à un processus inexistant, ce qui signifie que l'enfant peut s'être arrêté.

1
user3898515

J'ai utilisé cela récemment (merci à 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

À partir de là, on peut facilement extrapoler et avoir un déclencheur (toucher un fichier, envoyer un signal) et modifier les critères de comptage (nombre de fichiers touchés, etc.) pour répondre à ce déclencheur. Ou si vous voulez juste "n'importe quel" rc non nul, supprimez simplement le verrou de save_status.

1
Lloeki

J'avais besoin de cela, mais le processus cible n'était pas un enfant du shell actuel, auquel cas wait $PID ne fonctionne pas. J'ai plutôt trouvé l'alternative suivante:

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

Cela repose sur la présence de procfs , qui peut ne pas être disponible (Mac ne le fournit pas par exemple). Donc, pour la portabilité, vous pouvez utiliser ceci à la place:

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

C'est quelque chose que j'utilise:

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

Je pense que le moyen le plus simple d'exécuter des tâches en parallèle et de vérifier le statut consiste à utiliser des fichiers temporaires. Il existe déjà quelques réponses similaires (par exemple Nietzche-jou et mug896). 

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

Le code ci-dessus n'est pas thread-safe. Si vous craignez que le code ci-dessus ne s'exécute en même temps, il est préférable d'utiliser un nom de fichier unique, tel que fail. $$. La dernière ligne doit satisfaire à l'exigence: "retourner le code de sortie 1 lorsque l'un des sous-processus se termine par le code! = 0?" J'ai jeté une exigence supplémentaire là-bas pour nettoyer. Il a peut-être été plus clair de l'écrire comme ceci:

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

Voici un extrait similaire permettant de collecter les résultats de plusieurs travaux: je crée un répertoire temporaire, récapitule les résultats de toutes les sous-tâches dans un fichier séparé, puis les vide pour révision. Cela ne correspond pas vraiment à la question - je le lance en 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

Il peut y avoir un cas où le processus est terminé avant d'attendre le processus. Si nous déclenchons l'attente d'un processus déjà terminé, cela provoquera une erreur, car pid n'est pas un enfant de ce shell. Pour éviter de tels cas, la fonction suivante peut être utilisée pour déterminer si le processus est complet ou non:

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