2009-05-26 8 views
10

Ho qualche codice Perl che esegue uno script di shell per più parametri, per semplificare, mi limiterò a supporre che Ho codice che assomiglia a questo:Come posso fare in modo che Perl aspetti i processi figli avviati in background con system()?

for $p (@a){ 
    system("/path/to/file.sh $p&"); 
} 

mi piacerebbe fare alcune più cose dopo questo, ma non riesco a trovare un modo per attendere che tutti i processi figli finiscano prima di continuare.

Conversione del codice per utilizzare fork() sarebbe difficile. Non c'è un modo più semplice?

+0

Richiede il codice di ritorno effettivo da file.sh? Puoi cambiare lo script in modo che scriva su un file una volta completato? – mkb

+1

Perché convertire il codice in fork è difficile? Nascondi tutti i dettagli in una subroutine. Si potrebbe anche ridefinire system() per chiamare invece la nuova subroutine. –

+0

Probabilmente mi limiterò a dividere questa parte in uno script separato che esegue la foratura e chiamarla dalla chiamata di sistema ... –

risposta

16

utilizzando fork/exec/wait non è poi così male:

my @a = (1, 2, 3); 
for my $p (@a) { 
    my $pid = fork(); 
    if ($pid == -1) { 
     die; 
    } elsif ($pid == 0) { 
     exec '/bin/sleep', $p or die; 
    } 
} 
while (wait() != -1) {} 
print "Done\n"; 
+1

Non si sta verificando un fork fallito e si sta premendo il pid bareword su @pids, non su $ pid. L'uscita dovrebbe probabilmente essere un dado, se viene eseguita significa che l'exec è fallito. –

+0

Risolto. Grazie, non sono davvero un madrelingua di Perl. – Dave

+0

$ pid == -1 dovrebbe essere probabilmente definito! ($ Pid). –

8

La conversione in fork() potrebbe essere difficile, ma è lo strumento corretto. system() è una chiamata bloccante; stai ottenendo il comportamento non bloccante eseguendo una shell e dicendogli di eseguire i tuoi script in background. Ciò significa che Perl non ha idea di cosa possano essere i PID dei bambini, il che significa che il tuo script non sa cosa aspettare.

Si potrebbe provare a comunicare i PID fino allo script Perl, ma ciò rapidamente sfugge. Usa la forchetta().

13

Dovrai cambiare qualcosa, cambiare il codice per usare fork è probabilmente più semplice, ma se sei morto contro l'uso di fork, puoi usare uno script di shell wrapper che tocchi un file quando è finito e poi controlla il codice Perl per l'esistenza dei file.

Ecco l'involucro:

#!/bin/bash 

$* 

touch /tmp/$2.$PPID 

Il codice Perl sarà simile:

for my $p (@a){ 
    system("/path/to/wrapper.sh /path/to/file.sh $p &"); 
} 
while (@a) { 
    delete $a[0] if -f "/tmp/$a[0].$$"; 
} 

ma penso che il codice di biforcazione è più sicuro e più chiara:

my @pids; 
for my $p (@a) { 
    die "could not fork" unless defined(my $pid = fork);\ 
    unless ($pid) { #child execs 
     exec "/path/to/file.sh", $p; 
     die "exec of file.sh failed"; 
    } 
    push @pids, $pid; #parent stores children's pids 
} 

#wait for all children to finish 
for my $pid (@pids) { 
    waitpid $pid, 0; 
} 
+0

Questa è una risposta molto migliore. – zdim

+0

Apparentemente questo crea un sacco di processi zombi. https://en.wikipedia.org/wiki/Zombie_process –

+1

@PratyushRathore Non dovrebbe. Un risultato di zombi quando il processo genitore non chiama wait o waitpid. Come puoi vedere, il genitore chiama waitpid per un bambino che è uscito. Se stai ricevendo un sacco di zombi, allora stai facendo qualcosa di sbagliato o uno dei primi figli impiega molto tempo e i bambini successivi sono usciti (ma non sono stati mietuti). È possibile passare a 'while (waitpid (-1, WNOHANG)> 0) {sleep 1}' per raccogliere il prossimo figlio uscito. –