Как я могу запустить системную команду и умереть, если что-то записано в 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, но попробуйте провести стресс-тестирование с большими объемами данных и с более сложными нагрузками и условиями, которые вы когда-либо ожидали в реальной системе.