Как я могу прочитать вывод ошибок внешних команд в Perl?
в рамках большей программы Perl я проверяю выходы diff
команды входных файлов в папке против ссылочных файлов, где пустой вывод (совпадение) является результатом передачи, а любой вывод из diff является результатом сбоя.
проблема в том, что если целевая папка коротка по количеству ожидаемых файлов, исключение diff не выводится в качестве вывода, создавая ложные проходы.
Выход Пример:
diff: /testfolder/Test-02/test-output.2: No such file or directory
7 ответов
сами программы не могут выбрасывать "исключения", но они могут возвращать ненулевые коды ошибок. Вы можете проверить код ошибки программы, запущенной с помощью backticks или system()
в Perl с помощью $?:
$toPrint = "FAIL" if $?;
(добавьте эту строку перед циклом, что тесты @output
.)
вот ответ в perlfaq8: как я могу захватить STDERR из внешней команды?
существует три основных способа выполнения внешних команд:
system $cmd; # using system()
$output = `$cmd`; # using backticks (``)
open (PIPE, "cmd |"); # using open()
С system() оба STDOUT и STDERR будут идти в том же месте, что и STDOUT и STDERR скрипта, если команда system () не перенаправит их. Backticks и open () читают только STDOUT вашей команды.
вы также можете использовать функцию open3 () из IPC:: Open3. Benjamin Goldberg предоставляет пример кода:
чтобы захватить 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 отдельно, вы можете перенаправить их во временные файлы, запустить команду, а затем прочитать temp файлы:
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, чтобы быть dup STDOUT в вашей программе Perl и избежать вызова оболочки для перенаправления. Это не работает:
open(STDERR, ">&STDOUT");
$alloutput = `cmd args`; # stderr still escapes
это не удается, потому что open() заставляет STDERR идти туда, где STDOUT шел во время open(). Затем backticks заставляют STDOUT перейти к строке, но не изменяются STDERR (который все еще идет к старому STDOUT).
обратите внимание, что вы должны использовать синтаксис перенаправления Bourne shell (sh(1)) в backticks, а не csh(1)! Подробности о том, почему система Perl() и 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");
первая команда отправляет как стандартную, так и стандартную ошибку во временный файл. Вторая команда отправляет туда только старый стандартный вывод, и старая Стандартная ошибка появляется на старом стандарте.
предполагая, что ошибки diff заканчиваются на STDERR, если вы хотите изучить или зарегистрировать ошибки, я рекомендую модуль CPAN Capture:: Tiny:
use Capture::Tiny 'capture';
my ($out, $err) = capture { system($command) };
это похоже на backticks, но дает вам STDOUT и STDERR отдельно.
существует список интересных способов работы с выводом команды backticks-enclosed в сайт perldoc. Вам придется прокручивать вниз или искать "qx / STRING/" (без кавычек)
вы также можете выполнить прогон с выводом "diff-d", который облегчит чтение вашего кода.
foreach (
diff -d $args
){
если (/^только в/){
делать все();
}
}