2

所以我有以下代码,问题是它在所有子进程(排序/gzip)完成之前就退出了。如何指示 Perl 等待所有后代进程?

#!/usr/bin/perl


use strict;
use warnings;

sub systemBash {
  my $cmd = shift;
  my @args = ( "bash", "-c", $cmd );
  print "command ".$cmd."\n";
  system(@args);
  if($? != 0){
    die "Command ".Dumper(@args)." failed";
  }
}
print "start";
systemBash("yes |head -n 1000000|awk '{print rand()}' > >(sort |gzip -9 -c > /dev/null)");
print "done";
4

4 回答 4

0

I wrote the following hideous code to solve this problem:

sub findAllPIDs{
 my ($firstPID) = @_;
 my %hashPID;
 $hashPID{$firstPID}=1;
 my $found;
 do{
   $found=0;
   foreach my $key (keys %hashPID){
     my $cmd="ps -o pid --ppid ".$key;
     my $out=`$cmd`;
     foreach my $line (split("\n",$out)){
       $line =~ s/^\s+//;
       $line =~ s/\s+$//;
       if($line ne "PID"){
         if(!(exists $hashPID{$line})){
           $hashPID{$line}=1;
           $found=1;
         }
       }
     }
   }

 }while($found);
 return (keys %hashPID);
}

sub poll{
 my $proc=shift;
 while(1){
   my $cmd="ps  -o s --pid $proc";
   my $out=`$cmd`;
   my $found=0;
   my @arr=split("\n",$out);
   shift(@arr);
   foreach my $line (@arr){
     $line =~ s/^\s+//;
     $line =~ s/\s+$//;
     if($line ne "Z"  ){
       $found=1;
     }
   }
   if(!$found){
     return $proc;
   }
   sleep(2);
 }
}

sub systemBash {
 my $cmd = shift;
 my @args = ( "bash", "-c", $cmd );

 print "running ".$cmd."\n";
 if(!$fakeRun){
   my $pid=fork();
   if($pid == 0){
     exec(@args);
   }else{
     sleep(1);
     my @array=findAllPIDs($pid);
     foreach my $pid (@array){
       my $code =poll($pid);
     }
   }
 }
}
于 2012-06-29T19:08:31.033 回答
0

你将不得不重新设计你的方法或作弊。

Perl 的system()等待bash,它(如您所知)等待您的管道完成。但是,进程替换会运行与您的孩子“离婚”的命令bash及其通常的耐心。所以:

重新设计你的方法

  1. 找到一种方法使子流程成为真正的管道,而无需流程替换。
  2. 通过某种 IPC 让最终处理阶段信号 perl(例如,发送信号、写入字节、非阻塞、到命名管道等)

欺骗

您可以欺骗孩子、孙子等以影响上面的#2。

让子进程继承 a的写端,返回pipe()后关闭自己的写端副本。system()当所有后代终止时,读取端将选择可读。

您需要做一些扭曲才能使其工作,因为pipe()d 文件句柄通常受$^F. 就像是:

use POSIX qw(F_SETFD);

...
  # XXX - error checking omitted
  pipe(my $rd, my $wr);
  fcntl($wr, F_SETFD, 0);

  system(...);
  close($wr);

  readline($rd); # Will block until all inherited copies of $wr are closed

这种技术几乎不是万无一失的,但经常奏效。

于 2012-06-09T03:48:06.170 回答
0

为了让 Perl 等待一个进程,它首先需要知道它应该等待的进程的 PID。

因为这个例子中的子进程是由系统产生的(bash 命令),Perl 不知道它们,所以没有简单的方法来处理这个,即:你必须猜测那些子进程的 PID。

我建议你采用以下两种方法之一:要么尝试猜测链中最后一个进程的 PID 并等待它结束,要么将事件序列分解为单个命令,每个命令都会产生一个稍后“馈送”的输出" 到下一个命令(这样你根本不需要做任何等待 - 'system' 命令会为你做这件事)。

于 2012-06-08T17:45:12.823 回答
0

您可以将您的进程替换转换为 FIFO,并明确等待它完成。

yes |head -n 1000000|awk '{print rand()}' > >(sort |gzip -9 -c > /dev/null)

变成

mkfifo tmp-fifo; { sort |gzip -9 -c > /dev/null; } < tmp-fifo & pid=$! ; yes |head -n 1000000|awk '{print rand()}' > tmp-fifo; wait $pid; rm tmp-fifo

Perl 代码不需要更改。

来源: http: //lists.gnu.org/archive/html/bug-bash/2011-11/msg00137.html

于 2012-07-01T00:11:10.047 回答