如何使Perl等待使用system()在后台启动的子进程?

时间:2009-05-26 16:14:52

标签: linux perl wait child-process

我有一些Perl代码为多个参数执行shell脚本,为了简化,我假设我的代码看起来像这样:

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

之后我想做更多的事情,但是我找不到等待所有子进程完成的方法,然后再继续。

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

3 个答案:

答案 0 :(得分:16)

使用fork / 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";

答案 1 :(得分: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;
}

答案 2 :(得分:9)

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

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