2009-05-26 3 views
10

Ich habe einige Perl-Code, der ein Shell-Skript für mehrere Parameter ausgeführt wird, zu vereinfachen, nehme an, ich nur, dass ich Code, der wie folgt aussieht:Wie kann ich warten, dass Perl-Prozesse im Hintergrund mit system() gestartet werden?

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

Ich würde gerne ein paar mehr Dinge tun, nachdem das, aber ich kann keinen Weg finden, darauf zu warten, dass alle Kindprozesse beendet sind, bevor ich fortfahre.

Umwandlung des Codes zur Verwendung von fork() wäre schwierig. Gibt es keinen einfacheren Weg?

+0

Benötigen Sie den tatsächlichen Rückgabecode von file.sh? Können Sie das Skript so ändern, dass es nach Abschluss in eine Datei schreibt? – mkb

+1

Warum ist es schwierig, den Code in fork zu konvertieren? Verstecken Sie alle Details in einer Subroutine. Sie könnten sogar system() neu definieren, um stattdessen Ihre neue Subroutine aufzurufen. –

+0

Ich werde diesen Teil wahrscheinlich in ein separates Skript teilen, das die Verzweigung ausführt, und das vom Systemaufruf aufrufen ... –

Antwort

16

Mit Gabel/exec/warte ist nicht so schlecht:

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

Sie suchen nicht nach einer fehlgeschlagenen Verzweigung und Sie schieben die bareword-PID auf @pids, nicht $ pid. Der Ausgang sollte wahrscheinlich ein Würfel sein, wenn er ausgeführt wird, bedeutet das, dass der Exec fehlgeschlagen ist. –

+0

Behoben. Danke, ich bin nicht wirklich ein Muttersprachler von Perl. – Dave

+0

$ pid == -1 sollte stattdessen definiert werden ($ pid). –

8

Das Konvertieren in fork() ist möglicherweise schwierig, aber es ist das richtige Werkzeug. system() ist ein blockierender Aufruf; Sie erhalten das nicht blockierende Verhalten, indem Sie eine Shell ausführen und sie anweisen, Ihre Skripte im Hintergrund auszuführen. Das bedeutet, dass Perl keine Ahnung hat, was die PIDs der Kinder sein könnten, was bedeutet, dass Ihr Skript nicht weiß, worauf es warten soll.

Sie könnten versuchen, die PIDs mit dem Perl-Skript zu kommunizieren, aber das gerät schnell außer Kontrolle. Verwenden Sie Gabel().

13

Sie werden etwas ändern müssen, ändern Sie den Code, um zu verwenden Gabel ist wahrscheinlich einfacher, aber wenn Sie gegen Gabelung gesetzt sind, könnten Sie ein Wrapper Shell-Skript verwenden, das eine Datei berührt, wenn es fertig ist und dann Überprüfen Sie Ihren Perl-Code auf die Existenz der Dateien. Hier

ist die Verpackung:

#!/bin/bash 

$* 

touch /tmp/$2.$PPID 

Ihr Perl-Code würde wie folgt aussehen:

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

Aber ich denke, das Forking Code ist sicherer und klarer:

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

Dies ist eine viel bessere Antwort. – zdim

+0

Anscheinend macht dies eine Menge Zombie-Prozesse. https://en.wikipedia.org/wiki/Zombie_process –

+1

@ PratyushRathore Es sollte nicht. Ein Zombie entsteht, wenn der Elternprozess nicht wait oder waitpid aufruft. Wie Sie sehen können, ruft der Parent waitpid für ein Kind auf, das sich verlassen hat. Wenn du eine Horde Zombies bekommst, dann machst du entweder etwas falsch oder eines der ersten Kinder braucht eine lange Zeit und die späteren Kinder sind gegangen (aber nicht geerntet worden). Sie können zu while (waitpid (-1, WNOHANG)> 0) {sleep 1} 'wechseln, um das nächste erregte Kind zu ernten. –

Verwandte Themen