2009-07-15 14 views
38

Per massimizzare l'utilizzo della CPU (ho eseguito le cose su una Debian Lenny in EC2) ho un semplice script per avviare i lavori in parallelo:Attendere bash i processi in background in script per essere finito

#!/bin/bash 

for i in apache-200901*.log; do echo "Processing $i ..."; do_something_important; done & 
for i in apache-200902*.log; do echo "Processing $i ..."; do_something_important; done & 
for i in apache-200903*.log; do echo "Processing $i ..."; do_something_important; done & 
for i in apache-200904*.log; do echo "Processing $i ..."; do_something_important; done & 
... 

Sono abbastanza soddisfatto di questa soluzione di lavoro, ma non ho potuto capire come scrivere ulteriore codice che solo eseguita una volta tutti i cicli sono stati completati.

C'è un modo per ottenere il controllo di questo?

risposta

63

C'è un comando incorporato bash per questo.

wait [n ...] 
     Wait for each specified process and return its termination sta‐ 
     tus. Each n may be a process ID or a job specification; if a 
     job spec is given, all processes in that job’s pipeline are 
     waited for. If n is not given, all currently active child pro‐ 
     cesses are waited for, and the return status is zero. If n 
     specifies a non-existent process or job, the return status is 
     127. Otherwise, the return status is the exit status of the 
     last process or job waited for. 
+1

Questo è stato veloce e ha risolto il mio problema, ben meritato, grazie! – mark

+13

hint usa '' 'wait $ (jobs -p)' '' per aspettare i nuovi lavori creati. – lambacck

+0

grazie @lambacck – pabloa98

20

Utilizzando GNU parallelo renderà il vostro script ancora più breve e forse più efficace:

parallel 'echo "Processing "{}" ..."; do_something_important {}' ::: apache-*.log 

Questo verrà eseguito un lavoro per core della CPU e continuare a farlo fino a quando tutti i file vengono elaborati.

La soluzione sarà essenzialmente suddividere i lavori in gruppi prima di eseguire. Qui 32 posti di lavoro in 4 gruppi:

Simple scheduling

GNU parallela genera invece un nuovo processo quando si finisce - mantenendo la CPU attiva e risparmiando tempo:

GNU Parallel scheduling

Per saperne di più:

0

Questa è la mia soluzione grezza:

function run_task { 
     cmd=$1 
     output=$2 
     concurency=$3 
     if [ -f ${output}.done ]; then 
       # experiment already run 
       echo "Command already run: $cmd. Found output $output" 
       return 
     fi 
     count=`jobs -p | wc -l` 
     echo "New active task #$count: $cmd > $output" 
     $cmd > $output && touch $output.done & 
     stop=$(($count >= $concurency)) 
     while [ $stop -eq 1 ]; do 
       echo "Waiting for $count worker threads..." 
       sleep 1 
       count=`jobs -p | wc -l` 
       stop=$(($count > $concurency)) 
     done 
} 

L'idea è quella di utilizzare "posti di lavoro" per vedere quanti bambini sono attivi in ​​background e aspettare fino a questo numero scende (un bambino esce). Una volta che un bambino esiste, è possibile avviare l'attività successiva.

Come si può vedere, c'è anche un po 'di logica in più per evitare di eseguire gli stessi esperimenti/comandi più volte. Fa il lavoro per me .. Tuttavia, questa logica potrebbe essere saltata o ulteriormente migliorata (ad esempio, controllare la data/ora di creazione del file, i parametri di input, ecc.).

1

ho dovuto fare questo di recente e si è conclusa con la seguente soluzione:

while true; do 
    wait -n || { 
    code="$?" 
    ([[ $code = "127" ]] && exit 0 || exit "$code") 
    break 
    } 
done; 

Ecco come funziona:

wait -n uscite non appena uno dei (potenzialmente molti) i processi in background uscite. Si restituisce sempre true e il ciclo va avanti fino a:

  1. Codice di uscita 127: l'ultimo processo in background è uscito con successo.Nel caso , ignoriamo il codice di uscita ed esci dalla sub-shell con il codice 0.
  2. Qualsiasi lavoro in background non è riuscito. Usciamo dalla sotto-shell con quel codice di uscita.

Con set -e, questo garantisce che lo script terminerà in anticipo e passerà attraverso il codice di uscita di qualsiasi lavoro in background non riuscito.

Problemi correlati