2

したがって、次のコードがあります。問題は、すべての子プロセス(sort / 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

Perlがプロセスを待機するためには、最初に待機する必要のあるプロセスのPIDを知る必要があります。

この例の子プロセスはシステム(bashコマンド)によって生成されるため、Perlはそれらを認識しません。したがって、これを処理する簡単な方法はありません。つまり、これらの子プロセスのPIDを推測する必要があります。

2つのアプローチのいずれかを取ることをお勧めします。チェーンの最後のプロセスのPIDを推測して終了するのを待つか、イベントのシーケンスを単一のコマンドに分割して、それぞれが後で「フィード」される出力を生成します。 "次のコマンドに移動します(この方法では、待機を行う必要はまったくありません。'system'コマンドが自動的に実行します)。

于 2012-06-08T17:45:12.823 に答える
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

この問題を解決するために、次の恐ろしいコードを作成しました。

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

プロセス置換を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 に答える