10

我有一些 Perl 代码为多个参数执行 shell 脚本,为了简化,我假设我的代码如下所示:

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

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

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

4

3 回答 3

18

使用 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";
于 2009-05-26T16:33:32.883 回答
14

您将不得不更改某些内容,更改代码以使用 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;
}
于 2009-05-26T16:32:31.093 回答
9

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

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

于 2009-05-26T16:29:04.860 回答