2010-12-07 5 views
1

私は外部スクリプトを使ったPerlスクリプトを書いています。外部スクリプトは、特定のディレクトリから実行ので、私は次のように有用であることが分かっている必要があります。システムコマンドを実行して何かがSTDERRに書き込まれたら、どうやって死ぬことができますか?

use IPC::System::Simple qw(capture); 

my @args = ('external script path...', 'arg1', ...); 
my $out = capture([0], "cd $dir ; @args"); 

時には外部スクリプトはSTDERRにものを書き込みますが、それでも、私はこれらの時間とconfess(またはdie)を取得したい0を返します。

my ($out, $err) = cool_capture([0], "cd $dir ; @args"); 
say "Output was: $out"; 
if ($err) { 
die "Error: this was written to STDERR: $err"; 
} 

私は何をすることができます:私は外部スクリプトの戻り値をコントロールしていないので、私は多分、私はので、私はこのようなものがあるでしょうそのSTDERRをキャプチャすることができると思いましたか?

答えて

6

これはPerl FAQのcoveredです。

use IPC::Open3; 
use Symbol 'gensym'; 

my($wtr, $rdr, $err); 
$err = gensym; 

my $pid = open3($wtr, $rdr, $err, 'test_app'); 

waitpid($pid, 0); 
my $status = $? >> 8; 

my $stdout = <$rdr>; 
my $stderr = <$err>; 

print "out output: $stdout\n"; 
print "err output: $stderr\n"; 

print "Exit code: $status\n"; 

EDIT:終了コードをキャプチャ含むように更新要求当たりtest_appを推定

はstderrにstdoutと一つの行に一行を出力するプログラムです。また

waitpid($pid, 0); 
my $child_exit_status = $? >> 8; 

そして、あなたはその注意と警告のためにとにかくお読みくださいを言うどのperldoc IPC::Open3を求めている可能性があります。

+0

+1は、Perlのドキュメントで参照を与え、答えを提供します。 –

+0

楽しいと利益のためにドキュメントをコピー/貼り付け... – Sorpigal

+0

+1ありがとうございます。これを拡張して、 'test_app'の戻り値が有効であることを確認できますか?' capture([0]、...) 'のように' 0'ですか? –

1

重要な出力がstdoutおよび/またはstderrに書き込まれている場合、またはそのプロセスに対して読み取りと書き込みの両方を行っている場合。さまざまなブロッキングの問題を避けるためには、I/O処理にもっと注意する必要があります。

my ($wtr, $rdr, $err) ; 

my $pid = IPC::Open3::open3($wtr, $rdr, $err, @_); 

close($wtr); 

my $stdout = ''; 
my $stderr = ''; 

my $s = IO::Select->new; 

$s->add($rdr) if $rdr; 
$s->add($err) if $err; 

while (my @ready = $s->can_read) { 

    foreach my $ioh (@ready) { 

     my $bytes_read = sysread($ioh, my $chunk = '', 1024); 

     die "read error: $!" unless $bytes_read >= 0; 

     if ($bytes_read) { 
      ($ioh eq $rdr? $stdout: $stderr) .= $chunk; 
     } 
    else { 
      $s->remove($ioh); 
     } 
    } 
} 

my $pid1; 
for (;;) { 

    last if kill(0, $pid); 

    $pid1 = wait(); 
    # 
    # Wait until we see the process or -1 (no active processes); 
    # 
    last if ($pid1 == $pid || $pid1 <= 0); 
} 

プロセスをシャットダウンする前に読み終えてください。プロセスの標準に書いている場合は、上記のselectループに$ wtrとsyswriteを追加する必要があります。

EDIT

理由:

上記のは、おそらく単純な場合のためにやり過ぎです。この高度な入出力処理は、数Kを超えるデータを移動する可能性が高い場合に有効です。

たとえば、 'df'コマンドを実行していた場合は必要ありません。

しかし、stdin、stdout、またはstderrのいずれかのシステムバッファがいっぱいになると、ブロックする可能性が高くなり、状況がより複雑になることがあります。

子プロセスがstderrおよび/またはstdoutバッファをいっぱいにすると、ブロックされ、それらをクリアする可能性があります。しかし、stdoutまたはstderrから読み込む前にプロセスの終了を待っている場合は、デッドロックです。システムコールが終了せず、子プロセスが完了しないことが分かります。

stdinに書き込まれているにもかかわらず、子プロセスが入力を消費できない場合、同様のデッドロックの可能性があります。これは、子プロセスが入力とstdoutへの書き込みを消費している 'パイプ'の状況で特にそうです。

selectループでは、ブロッキングを回避するためにバッファが徐々にクリアされています。 stdoutとstderrの両方が同時に監視されます。

stdoutに書き込んでstdout(パイプ)から読み込む場合は、stdoutとstderrをクリアにし、入力を受け取る準備ができたらstdinに書き込むだけです。

プロセスが終了するのを待つだけで、stdout/stderrを読むのはおそらく時間の90%で行われます。この返信は、状況がより複雑になり、プロセスがブロックされたりデッドロックに陥ったりすると、どこかに行くことができます。私が言うと思います使用するためのEDIT2

は、ハード、単純な、テストを開始します。

Sorpigalのアプローチに従いますが、より高いデータ量と、実際のシステムで期待していたより困難な負荷と条件でストレステストを試みてください。

+0

これがなぜ必要なのか説明できますか? –

+0

@David B:いくつかの説明を追加しました。お役に立てれば。 – dwarring

関連する問題