Attendre que les processus d'arrière-plan soient terminés avant de quitter script

Comment puis-je m'assurer que tous mes processus d'arrière-plan ont terminé l'exécution avant de quitter mon script (TCL/Bash).

je pensais écrire tout mon processus de base pids à un pidfile. Et puis à la fin pgrep le fichier pidfile pour voir si des processus sont encore en cours d'exécution avant que je sorte.

y a-t-il un moyen plus simple de le faire? Et y a-t-il une façon spécifique de le faire?

50
demandé sur egorulz 2013-01-10 13:03:01

4 réponses

si vous voulez attendre la fin des travaux, utilisez wait . Cela fera attendre l'interpréteur de commandes jusqu'à ce que tous les travaux de fond soient terminés. Cependant, si l'un de vos jobs se démonise, il n'est plus enfant de la coquille et wait n'aura aucun effet (en ce qui concerne la coquille, l'enfant est déjà fait. En effet, lorsqu'un processus se démonte, il le fait en mettant fin à un nouveau processus qui hérite de son rôle).

#!/bin/sh
{ sleep 5; echo waking up after 5 seconds; } &
{ sleep 1; echo waking up after 1 second; } &
wait
echo all jobs are done!
113
répondu William Pursell 2016-07-11 14:15:38

vous pouvez utiliser kill -0 pour vérifier si un pid particulier est en cours d'exécution ou non.

en supposant que vous ayez la liste des numéros pid dans un fichier appelé pid dans pwd

while true;
do 
    if [ -s pid ] ; then
        for pid in `cat pid`
        do  
            echo "Checking the $pid"
            kill -0 "$pid" 2>/dev/null || sed -i "/^$pid$/d" pid
        done
    else
        echo "All your process completed" ## Do what you want here... here all your pids are in finished stated
        break
    fi
done
9
répondu Suku 2013-01-10 09:40:26

AVERTISSEMENT : Longue script à l'avance.

il y a un moment, j'ai fait face à un problème similaire: à partir d'un script Tcl, j'ai lancé un certain nombre de processus, puis j'ai attendu qu'ils soient tous terminés. Voici un script de démonstration que j'ai écrit pour résoudre ce problème.

principal.tcl

#!/usr/bin/env tclsh

# Launches many processes and wait for them to finish.
# This script will works on systems that has the ps command such as
# BSD, Linux, and OS X

package require Tclx; # For process-management utilities

proc updatePidList {stat} {
    global pidList
    global allFinished

    # Parse the process ID of the just-finished process
    lassign $stat processId howProcessEnded exitCode

    # Remove this process ID from the list of process IDs
    set pidList [lindex [intersect3 $pidList $processId] 0]
    set processCount [llength $pidList]

    # Occasionally, a child process quits but the signal was lost. This
    # block of code will go through the list of remaining process IDs
    # and remove those that has finished
    set updatedPidList {}
    foreach pid $pidList {
        if {![catch {exec ps $pid} errmsg]} {
            lappend updatedPidList $pid
        }
    }

    set pidList $updatedPidList

    # Show the remaining processes
    if {$processCount > 0} {
        puts "Waiting for [llength $pidList] processes"
    } else {
        set allFinished 1
        puts "All finished"
    }
}

# A signal handler that gets called when a child process finished.
# This handler needs to exit quickly, so it delegates the real works to
# the proc updatePidList
proc childTerminated {} {
    # Restart the handler
    signal -restart trap SIGCHLD childTerminated

    # Update the list of process IDs
    while {![catch {wait -nohang} stat] && $stat ne {}} {
        after idle [list updatePidList $stat]
    }
}

#
# Main starts here
#

puts "Main begins"
set NUMBER_OF_PROCESSES_TO_LAUNCH 10
set pidList {}
set allFinished 0

# When a child process exits, call proc childTerminated
signal -restart trap SIGCHLD childTerminated

# Spawn many processes
for {set i 0} {$i < $NUMBER_OF_PROCESSES_TO_LAUNCH} {incr i} {
    set childId [exec tclsh child.tcl $i &]
    puts "child #$i, pid=$childId"
    lappend pidList $childId
    after 1000
}

# Do some processing
puts "list of processes: $pidList"
puts "Waiting for child processes to finish"
# Do some more processing if required

# After all done, wait for all to finish before exiting
vwait allFinished

puts "Main ends"

de l'enfant.tcl

#!/usr/bin/env tclsh
# child script: simulate some lengthy operations

proc randomInteger {min max} {
    return [expr int(rand() * ($max - $min + 1) * 1000 + $min)]
}

set duration [randomInteger 10 30]
puts "  child #$argv runs for $duration miliseconds"
after $duration
puts "  child #$argv ends"

sortie D'échantillon pour l'exécution main.tcl

Main begins
child #0, pid=64525
  child #0 runs for 17466 miliseconds
child #1, pid=64526
  child #1 runs for 14181 miliseconds
child #2, pid=64527
  child #2 runs for 10856 miliseconds
child #3, pid=64528
  child #3 runs for 7464 miliseconds
child #4, pid=64529
  child #4 runs for 4034 miliseconds
child #5, pid=64531
  child #5 runs for 1068 miliseconds
child #6, pid=64532
  child #6 runs for 18571 miliseconds
  child #5 ends
child #7, pid=64534
  child #7 runs for 15374 miliseconds
child #8, pid=64535
  child #8 runs for 11996 miliseconds
  child #4 ends
child #9, pid=64536
  child #9 runs for 8694 miliseconds
list of processes: 64525 64526 64527 64528 64529 64531 64532 64534 64535 64536
Waiting for child processes to finish
Waiting for 8 processes
Waiting for 8 processes
  child #3 ends
Waiting for 7 processes
  child #2 ends
Waiting for 6 processes
  child #1 ends
Waiting for 5 processes
  child #0 ends
Waiting for 4 processes
  child #9 ends
Waiting for 3 processes
  child #8 ends
Waiting for 2 processes
  child #7 ends
Waiting for 1 processes
  child #6 ends
All finished
Main ends
2
répondu Hai Vu 2013-01-10 20:38:19

GNU parallel et xargs sont deux outils qui peuvent simplifier les scripts et contrôler le nombre maximum de threads (thread pool). Par exemple:

seq 10 | xargs -P4 -I'{}' echo '{}'

ou:

seq 10 | parallel -j4  echo '{}'

Voir aussi: comment écrire un processus de-piscine shell bash

1