Как я могу запустить системную команду и умереть, если что-то записано в 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";
}

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

2 ответа

Решение

Это описано в Perl FAQ.

Предположив test_app это программа, которая выводит одну строку в stdout и одну строку в stderr:

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;

И что вы должны прочитать в любом случае за его предостережения и предостережения.

Если значительный вывод записывается в 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 в вышеупомянутый цикл выбора.

РЕДАКТИРОВАТЬ

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

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

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

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

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

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

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

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

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

EDIT2

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

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

Другие вопросы по тегам