2008-09-20 7 views
56

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

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

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

ответ

26

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

+1

Я не знаю, почему ссылки прикручены. Все отлично работает в edit/preview. – 2008-09-20 19:57:39

+2

Я отправил perl5porters патч для IPC :: Open2 и :: Open3, чтобы показать файл waitpid в SYNOPSIS этих модулей. :) – 2008-09-21 21:56:25

7

Есть три основных способа выполнения внешних команд:

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

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

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

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

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

+0

Не забудьте `qx //` – 2017-04-06 07:36:07

41

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

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

use IO::CaptureOutput qw/capture_exec/; 

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

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

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

+12

Появляется [Capture :: Tiny] (http://search.cpan.org/dist/Capture-Tiny/) новее и лучше: «Этот модуль был вдохновлен с помощью IO :: CaptureOutput, которая обеспечивает аналогичную функциональность без возможности выхода на печать и с более сложным кодом и API. IO :: CaptureOutput не обрабатывает слои или большинство необычных случаев, описанных в разделе «Ограничения», и я больше не рекомендую его ». - http://search.cpan.org/~dagolden/Capture-Tiny-0.18/lib/Capture/Tiny.pm#SEE_ALSO – 2012-07-31 20:18:30

14

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

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

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

    my $exit_value = $EXITVAL; 

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

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

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 обеспечивает чистый и простой интерфейс для повторного монтажа всех трех обычных дескрипторов файлов, но, к сожалению, он не проверяет, была ли команда успешной, поэтому вам нужно будет проверить $? вручную, что совсем не весело. Предоставление открытого интерфейса для проверки $? это что-то, что находится на моем to-do list для IPC :: System :: Simple, так как проверка $? в кросс-платформенной моде - это не задача, которую я хотел бы пожелать никому.

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

Все самое лучшее,

Пол

0

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

0

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

Ниже приведено то, как я схватил stderr, которого я знал, было бы числом. CMD выводит информационные преобразования в stdout (которые я передал в файл в args using>) и сообщил, сколько преобразований в STDERR.

use IPC::Run3 

my $number; 
my $run = run3("cmd arg1 arg2 >output_file",\undef, \undef, \$number); 
die "Command failed: $!" unless ($run && $? == 0);