Как вы одновременно захватываете stderr, stdout и код выхода в Perl? - PullRequest
56 голосов
/ 20 сентября 2008

Можно ли запустить внешний процесс из Perl, перехватить его stderr, stdout И код завершения процесса?

Кажется, я могу комбинировать их, например, используйте backticks для получения stdout, IPC :: Open3 для захвата выходных данных и system () для получения кодов выхода.

Как вы одновременно захватываете stderr, stdout и код выхода?

Ответы [ 6 ]

41 голосов
/ 21 сентября 2008

( Обновление : я обновил API для IO :: CaptureOutput, чтобы сделать это еще проще.)

Есть несколько способов сделать это. Вот один вариант, использующий модуль IO :: CaptureOutput :

use IO::CaptureOutput qw/capture_exec/;

my ($stdout, $stderr, $success, $exit_code) = capture_exec( @cmd );

Это функция capture_exec (), но IO :: CaptureOutput также имеет более общую функцию capture (), которую можно использовать для захвата либо вывода Perl, либо вывода из внешних программ. Так что, если какой-то модуль Perl использует какую-то внешнюю программу, вы все равно получите вывод.

Это также означает, что вам нужно запомнить только один единственный подход к захвату STDOUT и STDERR (или объединению их) вместо использования IPC :: Open3 для внешних программ и других модулей для захвата вывода Perl.

26 голосов
/ 20 сентября 2008

Если вы перечитаете документацию для IPC :: Open3, вы увидите примечание, что вам нужно вызвать waitpid , чтобы пожинать дочерний процесс. Как только вы это сделаете, статус должен быть доступен в $?. Выходное значение $? >> 8. Увидеть $? в perldoc perlvar .

14 голосов
/ 21 сентября 2008

Если вам не нужно содержимое STDERR, то команда capture () из модуля IPC :: System :: Simple - это почти то, что вам нужно:

   use IPC::System::Simple qw(capture system $EXITVAL);

   my $output = capture($cmd, @args);

   my $exit_value = $EXITVAL;

Вы можете использовать метод capture () с одним аргументом для вызова оболочки или несколькими аргументами для надежного обхода оболочки. Также есть метод capturex (), который никогда не вызывает оболочку, даже с одним аргументом.

В отличие от встроенной в Perl команды system и backticks, IPC :: System :: Simple возвращает полное 32-битное значение выхода в Windows. Он также выдает подробное исключение, если команда не может быть запущена, умирает от сигнала или возвращает неожиданное значение выхода. Это означает, что для многих программ, вместо того, чтобы проверять значения выхода самостоятельно, вы можете положиться на IPC :: System :: Просто сделать тяжелую работу за вас:

 use IPC::System::Simple qw(system capture $EXIT_ANY);

 system( [0,1], "frobincate", @files);     # Must return exitval 0 or 1

 my @lines = capture($EXIT_ANY, "baznicate", @files);  # Any exitval is OK.

 foreach my $record (@lines) {
     system( [0, 32], "barnicate", $record);  # Must return exitval 0 or 32
 }

IPC :: System :: Simple - это чистый Perl, не имеет зависимостей и работает как в системах Unix, так и в Windows. К сожалению, он не обеспечивает способ захвата STDERR, поэтому он может не подходить для всех ваших потребностей.

IPC :: Run3 предоставляет простой и понятный интерфейс для повторного подключения всех трех общих файловых дескрипторов, но, к сожалению, он не проверяет, была ли команда выполнена успешно, поэтому вам нужно проверить $? вручную, что совсем не весело. Предоставление открытого интерфейса для проверки $? что-то, что находится в моем списке дел для IPC :: System :: Simple, начиная с проверки $? в кроссплатформенном стиле это не та задача, которую я желал бы никому.

В пространстве имен IPC :: есть и другие модули, которые также могут оказать вам помощь. YMMV.

Всего наилучшего,

Пол

7 голосов
/ 20 сентября 2008

Существует три основных способа запуска внешних команд:

system $cmd;        # using system()
$output = `$cmd`;       # using backticks (``)
open (PIPE, "cmd |");   # using open()

При system() и STDOUT, и STDERR займут то же место, что и скрипты STDOUT и STDERR,, если только команда system() не перенаправит их. Обратные пометки и open() только для чтения STDOUT вашей команды.

Вы также можете вызвать что-то вроде следующего с помощью open, чтобы перенаправить как STDOUT, так и STDERR.

open(PIPE, "cmd 2>&1 |");

Код возврата всегда сохраняется в $?, как отмечено @ Michael Carman .

0 голосов
/ 25 апреля 2012

Я нашел IPC: run3 , чтобы быть очень полезным. Вы можете переслать все дочерние каналы в глоб или переменную; очень легко! И код выхода будет храниться в $?.

Ниже показано, как я схватил stderr, который, как я знал, будет числом. Команда cmd выводит информативные преобразования в стандартный вывод (который я передал в файл в аргументах с помощью>) и сообщает, сколько преобразований в STDERR.

use IPC::Run3

my $number;
my $run = run3("cmd arg1 arg2 >output_file",\undef, \undef, \$number);
die "Command failed: $!" unless ($run && $? == 0);
0 голосов
/ 21 сентября 2008

Если вам действительно сложно, вы можете попробовать Expect.pm. Но это, вероятно, излишне, если вам также не нужно управлять отправкой входных данных процессу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...