2

次のスクリプトを使用してキャプチャしSTDIN、引数として渡されたコマンドから取得しています。STDOUTSTDERR

#!/usr/bin/perl

use strict;
use warnings;

use IPC::Open3;

local(*CMD_IN, *CMD_OUT, *CMD_ERR);

my $pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $ARGV[0]); 

close(CMD_IN);

my @stdout_output = <CMD_OUT>;
my @stderr_output = <CMD_ERR>; 

close(CMD_OUT);
close(CMD_ERR);

waitpid ($pid, 0); # reap the exit code

print "OUT:\n", @stdout_output;
print "ERR:\n", @stderr_output;

渡されたコマンドがハングしているかどうかを監視する方法がわからないことを除いて、すべてうまく機能します。方法を教えてください。

このスニペットは、もともと「プログラミング Perl」から借りてきました。

4

2 に答える 2

6

selectまたはIO::Selectを使用して、タイムアウトを指定できます。stdout と stderr の両方から読み取りたい場合は、とにかくそれを行う必要があります (のドキュメントを参照してくださいIPC::Open3)。

を使用したプログラムの例を次に示しますIO::Select

#!/usr/bin/perl

use strict;
use warnings;

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

my ($cmd_in, $cmd_out, $cmd_err);
$cmd_err = gensym;
my $pid = open3($cmd_in, $cmd_out, $cmd_err, $ARGV[0]);

close($cmd_in);

my $select = IO::Select->new($cmd_out, $cmd_err);
my $stdout_output = '';
my $stderr_output = '';

while (my @ready = $select->can_read(5)) {
    foreach my $handle (@ready) {
        if (sysread($handle, my $buf, 4096)) {
            if ($handle == $cmd_out) {
                $stdout_output .= $buf;
            }
            else {
                $stderr_output .= $buf;
            }
        }
        else {
            # EOF or error
            $select->remove($handle);
        }
    }
}

if ($select->count) {
    print "Timed out\n";
    kill('TERM', $pid);
}

close($cmd_out);
close($cmd_err);

waitpid($pid, 0); # reap the exit code

print "OUT:\n", $stdout_output;
print "ERR:\n", $stderr_output;

ノート:

  • ファイルハンドルには字句変数を使用します。これにはgensym、stderr ハンドルに を使用する必要があります。
  • への引数can_readは秒単位のタイムアウトです。
  • sysread非バッファIOに使用します。
  • 読み取りタイムアウトがある場合は、子を終了します。
于 2013-08-22T10:29:19.580 に答える
1

この回答に大きく基づいて、次の解決策を思いつきました。

ただしselect、nwellnhof の例のようにシグナルを使用および回避すると、はるかにクリーンに見えるため、それを受け入れました。誰かが興味を持っている場合は、ここに投稿します:

my $pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $cmd);

if ($pid > 0){
    eval{
        local $SIG{ALRM} = sub {kill 9, $pid;};
        alarm 6;
        waitpid($pid, 0);
        alarm 0;
    };
}
于 2013-08-22T11:21:29.437 に答える