Как я могу запустить системную команду и умереть, если что-то записано в STDERR? - PullRequest
1 голос
/ 07 декабря 2010

Я пишу 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";
}

Что я могу сделать?

Ответы [ 2 ]

6 голосов
/ 07 декабря 2010

Это покрыто в Perl FAQ.

Предполагается, что test_app - это программа, которая выводит одну строку в стандартный вывод и одну строку в стандартный вывод:

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";

РЕДАКТИРОВАТЬ: В соответствии с запросом обновлен, чтобы включить захват кода выхода. Вы могли бы также спросить perldoc IPC::Open3, который говорит

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

А что вам следует прочитать в любом случае, из-за предостережений и предостережений.

1 голос
/ 07 декабря 2010

Если значительный вывод записывается в stdout и / или stderr, или вы оба читаете и пишете в процесс.Вы должны быть намного осторожнее с обработкой ввода / вывода, чтобы избежать различных проблем с блокировкой.

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 в вышеуказанный цикл выбора.

EDIT

Обоснование:

Вышесказанное, вероятно, излишне для простых случаев.Эта продвинутая обработка ввода и вывода вступает в игру, когда вы, вероятно, переместите больше, чем несколько килограмм данных.

Вам это не понадобится, если вы выполняете команду 'df', например.

Однако, именно тогда, когда системные буферы для любого из stdin, stdout или stderr заполняются, блокировка становится вероятной, и дела могут стать более активными.

Если дочерний процесс заполняет stderr и / или stdoutбуферы, скорее всего, они будут блокироваться и ждать, пока вы их очистите.Но если вы ждете окончания процесса, прежде чем начнете читать с stdout или stderr;это тупик.Вы, вероятно, увидите, что системный вызов никогда не завершается, а дочерний процесс никогда не завершается.

Существует аналогичная вероятность тупиковой ситуации, если в stdin выполняется запись, но дочерний процесс не может использовать входные данные.Это особенно вероятно в ситуации «канала», когда дочерний процесс потребляет ввод и запись в стандартный вывод.

Цикл выбора предназначен для постепенной очистки буферов, чтобы избежать блокировки.Как stdout, так и stderr контролируются одновременно.

Если вы пишете в stdin и читаете из stdout (канал), вам нужно будет очистить stdout и stderr и писать в stdin, только когда он готов к приемуinput.

Просто ожидание завершения процесса, тогда чтение stdout / stderr, вероятно, работает 90% времени.Этот ответ просто для того, чтобы вам было куда идти, если что-то усложняется, а процессы начинают блокироваться или переходить в тупик.

EDIT2

Что касается использования,Я бы сказал, начните с простого, тестируйте с большим трудом.

Придерживайтесь подхода Сорпигала, но попробуйте провести стресс-тестирование с большими объемами данных и с более сложными нагрузками и условиями, которые вы когда-либо ожидали в работающей системе.

...