См. http://perldoc.perl.org/perlfaq8.html#How-can-I-capture-STDERR-from-an-external-command%3f
Как я могу захватить STDERR из внешней команды?
Существует три основных способа запуска внешних команд:
system $cmd; # using system()
$output = `$cmd`; # using backticks (``)
open (PIPE, "cmd |"); # using open()
При использовании system () и STDOUT, и STDERR будут находиться в том же месте, что и сценарии STDOUT и STDERR, если только команда system () не перенаправит их. Обратные метки и open () только для чтения STDOUT вашей команды.
Вы также можете использовать функцию open3 () из IPC :: Open3. Бенджамин Голдберг предоставляет пример кода:
Чтобы захватить STDOUT программы, но сбросить ее STDERR:
use IPC::Open3;
use File::Spec;
use Symbol qw(gensym);
open(NULL, ">", File::Spec->devnull);
my $pid = open3(gensym, \*PH, ">&NULL", "cmd");
while( <PH> ) { }
waitpid($pid, 0);
Чтобы захватить STDERR программы, но сбросить ее STDOUT:
use IPC::Open3;
use File::Spec;
use Symbol qw(gensym);
open(NULL, ">", File::Spec->devnull);
my $pid = open3(gensym, ">&NULL", \*PH, "cmd");
while( <PH> ) { }
waitpid($pid, 0);
Для захвата STDERR программы и передачи ее STDOUT в наш собственный STDERR:
use IPC::Open3;
use Symbol qw(gensym);
my $pid = open3(gensym, ">&STDERR", \*PH, "cmd");
while( <PH> ) { }
waitpid($pid, 0);
Чтобы прочитать STDOUT команды и ее STDERR по отдельности, вы можете перенаправить их во временные файлы, запустить команду, а затем прочитать временные файлы:
use IPC::Open3;
use Symbol qw(gensym);
use IO::File;
local *CATCHOUT = IO::File->new_tmpfile;
local *CATCHERR = IO::File->new_tmpfile;
my $pid = open3(gensym, ">&CATCHOUT", ">&CATCHERR", "cmd");
waitpid($pid, 0);
seek $_, 0, 0 for \*CATCHOUT, \*CATCHERR;
while( <CATCHOUT> ) {}
while( <CATCHERR> ) {}
Но нет никакой необходимости, чтобы оба были tempfiles ... следующее должно работать точно так же, без взаимоблокировки:
use IPC::Open3;
use Symbol qw(gensym);
use IO::File;
local *CATCHERR = IO::File->new_tmpfile;
my $pid = open3(gensym, \*CATCHOUT, ">&CATCHERR", "cmd");
while( <CATCHOUT> ) {}
waitpid($pid, 0);
seek CATCHERR, 0, 0;
while( <CATCHERR> ) {}
И это будет быстрее, так как мы можем начать обработку stdout программы немедленно, а не ждать завершения программы.
С любым из них вы можете изменить дескрипторы файлов перед вызовом:
open(STDOUT, ">logfile");
system("ls");
или вы можете использовать перенаправление файлов-дескрипторов оболочки Bourne:
$output = `$cmd 2>some_file`;
open (PIPE, "cmd 2>some_file |");
Вы также можете использовать перенаправление файлового дескриптора, чтобы сделать STDERR дубликатом STDOUT:
$output = `$cmd 2>&1`;
open (PIPE, "cmd 2>&1 |");
Обратите внимание, что вы не можете просто открыть STDERR для дублирования STDOUT в вашей Perl-программе и избегать вызова оболочки для выполнения перенаправления. Это не работает:
open(STDERR, ">&STDOUT");
$alloutput = `cmd args`; # stderr still escapes
Это не удалось, потому что open () заставляет STDERR идти туда, куда STDOUT шел во время open (). Затем обратные метки заставляют STDOUT переходить к строке, но не изменяют STDERR (который все еще идет к старому STDOUT).
Обратите внимание, что вы должны использовать синтаксис перенаправления оболочки Bourne (sh (1)) в обратных галочках, а не csh (1)! Подробная информация о том, почему Perl system () и backtick и pipe открывают все, использующие оболочку Bourne, находится в статье versus / csh.whynot в коллекции «Гораздо больше, чем вы хотели знать» в http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz Чтобы захватить STDERR и STDOUT команды вместе:
$output = `cmd 2>&1`; # either with backticks
$pid = open(PH, "cmd 2>&1 |"); # or with an open pipe
while (<PH>) { } # plus a read
Чтобы захватить STDOUT команды, но сбросить ее STDERR:
$output = `cmd 2>/dev/null`; # either with backticks
$pid = open(PH, "cmd 2>/dev/null |"); # or with an open pipe
while (<PH>) { } # plus a read
Чтобы захватить STDERR команды, но сбросить ее STDOUT:
$output = `cmd 2>&1 1>/dev/null`; # either with backticks
$pid = open(PH, "cmd 2>&1 1>/dev/null |"); # or with an open pipe
while (<PH>) { } # plus a read
Чтобы заменить STDOUT и STDERR команды, чтобы захватить STDERR, но оставить STDOUT для выхода из нашего старого STDERR:
$output = `cmd 3>&1 1>&2 2>&3 3>&-`; # either with backticks
$pid = open(PH, "cmd 3>&1 1>&2 2>&3 3>&-|");# or with an open pipe
while (<PH>) { } # plus a read
Чтобы прочитать STDOUT команды и ее STDERR по отдельности, проще всего перенаправить их отдельно в файлы, а затем прочитать из этих файлов после завершения программы:
system("program args 1>program.stdout 2>program.stderr");
Заказ важен во всех этих примерах. Это связано с тем, что оболочка обрабатывает перенаправления файловых дескрипторов строго в порядке слева направо.
system("prog args 1>tmpfile 2>&1");
system("prog args 2>&1 1>tmpfile");
Первая команда отправляет как стандартную ошибку, так и стандартную ошибку во временный файл. Вторая команда отправляет туда только старый стандартный вывод, и старая стандартная ошибка обнаруживается при выходе старого стандарта.