2009-05-26 10 views
10

Tengo algo de código Perl que ejecuta un script de shell para múltiples parámetros, para simplificar, sólo voy a asumir que tengo código que se parece a esto:¿Cómo puedo hacer que Perl espere a que los procesos secundarios se inicien en segundo plano con system()?

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

me gustaría hacer algunas cosas más después eso, pero no puedo encontrar una manera de esperar a que terminen todos los procesos hijos antes de continuar.

Convertir el código para usar fork() sería difícil. ¿No hay una manera más fácil?

+0

¿Necesita el código de retorno real de file.sh? ¿Puedes cambiar la secuencia de comandos para que se escriba en un archivo cuando se complete? – mkb

+1

¿Por qué sería difícil convertir el código en fork? Ocultar todos los detalles en una subrutina. Incluso podría redefinir el sistema() para llamar a su nueva subrutina en su lugar. –

+0

Probablemente voy a dividir esta parte en una secuencia de comandos separada que hace la bifurcación, y llamar a eso desde la llamada al sistema ... –

Respuesta

16

Usando tenedor/exec/wait no es tan malo:

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

No está buscando una horquilla fallida y está presionando la palabra pid desnuda en @pids, no $ pid. La salida probablemente debería ser un dado en su lugar, si se ejecuta eso significa que el ejecutivo falló. –

+0

Reparado. Gracias, realmente no soy un hablante nativo de Perl. – Dave

+0

$ pid == -1 probablemente debería definirse ($ pid) en su lugar. –

8

La conversión a la horquilla() puede ser difícil, pero es la herramienta correcta. system() es una llamada de bloqueo; obtiene el comportamiento de no bloqueo ejecutando un shell y diciéndole que ejecute sus scripts en segundo plano. Eso significa que Perl no tiene idea de cuáles podrían ser los PID de los niños, lo que significa que su secuencia de comandos no sabe qué esperar.

Puede tratar de comunicar los PID hasta la secuencia de comandos de Perl, pero eso se va de las manos. Usa la horquilla().

13

Vas a tener que cambiar algo, cambiar el código para usar fork es probablemente más simple, pero si estás en contra del uso del tenedor, podrías usar un guión que toque un archivo cuando esté listo y luego haga que su código Perl verifique la existencia de los archivos.

Aquí es el envoltorio:

#!/bin/bash 

$* 

touch /tmp/$2.$PPID 

Su código Perl se vería así:

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

pero creo que el código de bifurcación es más seguro y más claro:

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

Esta es una respuesta mucho mejor. – zdim

+0

Aparentemente, esto crea muchos procesos zombies. https://en.wikipedia.org/wiki/Zombie_process –

+1

@PratyushRathore No debería. Se produce un zombie cuando el proceso principal no llama a wait o waitpid. Como puede ver, el padre está llamando a waitpid para un niño que ha salido. Si obtienes un grupo de zombis, entonces estás haciendo algo mal o uno de los primeros está tardando mucho tiempo y los últimos niños han salido (pero no han sido cosechados). Puede cambiar a 'while (waitpid (-1, WNOHANG)> 0) {sleep 1}' para obtener el siguiente hijo salido. –

Cuestiones relacionadas