2010-12-07 2 views
1

Я пишу скрипт Perl, который использует внешний скрипт. Внешний скрипт должен запускаться из определенного каталога, так что я нашел следующее полезным:Как я могу запустить системную команду и умереть, если что-нибудь написано в STDERR?

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

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

ответ

6

Это covered в Перл 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"; 

EDIT: За просьбу обновляемой включить захватывая код выхода. Вы также могли бы спросил perldoc IPC::Open3, который говорит

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

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

+0

+1 для предоставления как ссылки в документе Perl, так и предоставления ответа. – 2010-12-07 16:02:38

+0

Копирование/вставка документации для удовольствия и прибыли ... – Sorpigal 2010-12-07 17:13:02

1

Если значительный результат записывается в 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); 
} 

Завершить чтение перед завершением процесса. Если вы пишете на stdin этого процесса, вам также необходимо добавить $ wtr и syswrite в цикл выше выбора.

EDIT

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

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

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

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

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

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

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

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

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

EDIT2

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

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

Смежные вопросы