システムコマンドを実行して、STDERR に何かが書き込まれた場合に停止するにはどうすればよいですか?
質問
外部スクリプトを使用する Perl スクリプトを作成しています。外部スクリプトは特定のディレクトリから実行する必要があるため、次のものが便利であることがわかりました。
use IPC::System::Simple qw(capture);
my @args = ('external script path...', 'arg1', ...);
my $out = capture( [0], "cd $dir ; @args" );
場合によっては、外部スクリプトが STDERR に内容を書き込んでも 0 を返すことがあります。この時代を捉えたいと思い、 confess
(または die
)。私は外部スクリプトの戻り値を制御していないので、その STDERR をキャプチャできるかもしれないと考え、次のようなものにします。
my ($out, $err) = cool_capture( [0], "cd $dir ; @args" );
say "Output was: $out";
if ($err) {
die "Error: this was written to STDERR: $err";
}
私に何ができる?
解決
これは カバーされています Perl FAQで。
推測 test_app
STDOUTに1行を出力し、STDERに1行を出力するプログラムです。
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";
編集:exitコードのキャプチャを含むように更新された要求に応じて。尋ねることもできます perldoc IPC::Open3
それは言う
waitpid( $pid, 0 );
my $child_exit_status = $? >> 8;
そして、あなたはとにかくその注意と警告のために読むべきです。
他のヒント
重要な出力が 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);
}
プロセスをシャットダウンする前に読み終えてください。プロセスの標準入力に書き込む場合は、上記の選択ループに $wtr と syswrite を追加する必要もあります。
編集
理論的根拠:
上記は、単純な場合にはおそらくやりすぎです。この入出力の高度な処理は、数 K を超えるデータを移動する可能性がある場合に役立ちます。
たとえば、「df」コマンドを実行する場合は必要ありません。
ただし、stdin、stdout、または stderr のいずれかのシステム バッファがいっぱいになると、ブロッキングが発生しやすくなり、事態がより複雑になる可能性があります。
子プロセスが stderr バッファや stdout バッファをいっぱいにすると、ブロックされ、クリアされるまで待機する可能性があります。ただし、stdout または stderr から読み取る前にプロセスの終了を待っている場合は、それは行き詰まりです。システムコールが終了せず、子プロセスも完了しないことがわかります。
標準入力に書き込まれているが、子プロセスが入力を消費できない場合にも、同様にデッドロックが発生する可能性があります。これは、子プロセスが入力を消費して stdout に書き込む「パイプ」状況で特に発生する可能性があります。
選択ループは、ブロックを回避するためにバッファーを段階的にクリアすることを目的としています。stdout と stderr の両方が同時に監視されます。
stdin に書き込み、stdout (パイプ) から読み取る場合は、stdout と stderr をクリアな状態に保ち、入力を受け取る準備ができたときにのみ stdin に書き込みます。
プロセスが完了するのを待って、stdout/stderr を読み取るだけで、おそらく 90% の確率で機能します。この返信は、状況がさらに複雑になり、プロセスがブロックまたはデッドロックになり始めた場合に、どこへ行くべきかを示すだけです。
編集2
どちらを使用するかについては、簡単に始めて、しっかりとテストすることをお勧めします。
Sorpigal のアプローチを採用しますが、実際のシステムで予想されるよりも多くのデータ量、より困難な負荷と条件下でストレス テストを行うようにしてください。