2009-05-26 68 views
10

我有一些Perl代码,对于多个参数来执行一个shell脚本,以简化,我就认为我有代码看起来像这样:如何让Perl等待使用system()在后台启动的子进程?

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

我想以后做一些更多的事情但是,我无法找到一种方法来等待所有子进程完成后再继续。

将代码转换为使用fork()会很困难。没有更简单的方法吗?

+0

你是否需要file.sh的实际返回码?您可以更改脚本,以便在完成后写入文件吗? – mkb 2009-05-26 16:18:58

+1

为什么要将代码转换为fork很困难?隐藏子例程中的所有细节。你甚至可以重新定义system()来代替调用你的新子程序。 – 2009-05-26 16:35:23

+0

我可能只是将这个部分拆分为一个单独的脚本,执行分叉操作,并从系统调用中调用该脚本... – 2009-05-26 17:51:00

回答

16

使用叉子/ exec/wait不是那么糟:

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"; 
8

转换为fork()可能很困难,但它是正确的工具。 system()是一个阻塞调用;你通过执行一个shell并告诉它在后台运行脚本来获得非阻塞行为。这意味着Perl不知道孩子的PID可能是什么,这意味着你的脚本不知道该等什么。

您可以尝试将PID传递给Perl脚本,但这很快就失控了。使用fork()。

13

你将不得不改变一些东西,改变使用fork的代码可能更简单,但是如果你对使用fork不感兴趣,你可以使用一个包装shell脚本,当它完成时触及一个文件,然后让你的Perl代码检查文件的存在。

这里是包装:

#!/bin/bash 

$* 

touch /tmp/$2.$PPID 

你的Perl代码看起来像:

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

但我认为分岔代码更安全,更清晰:

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; 
}