如何读取 Perl 中外部命令的错误输出?

发布于 2024-07-18 05:35:08 字数 622 浏览 5 评论 0 原文

作为较大 Perl 程序的一部分,我正在对照参考文件检查文件夹中输入文件的 diff 命令的输出,其中空白输出(匹配)是通过结果,并且来自diff 是失败结果。

问题是,如果目标文件夹缺少预期文件的数量,则抛出的异常 diff 不会作为输出出现,从而创建错误的传递。

输出示例:

diff: /testfolder/Test-02/test-output.2: No such file or directory

Test-01: PASS

Test-02: PASS

代码如下:

$command = "(diff call on 2 files)";
my @output = `$command`;
print "Test-02: ";
$toPrint = "PASS";
foreach my $x (@output) {
    if ($x =~ /./) {
        $toPrint = "FAIL";
    }
}

如果 diff 调用有任何输出,这是一项快速的黑客工作,很快就会失败。 有没有办法检查反引号中调用的命令抛出的异常?

As part of a larger Perl program, I am checking the outputs of diff commands of input files in a folder against reference files, where a blank output (a match) is a passing result, and any output from diff is a fail result.

The issue is, if the target folder is short on the number of expected files, the exception diff throws doesn't come as output, creating false passes.

Output Example:

diff: /testfolder/Test-02/test-output.2: No such file or directory

Test-01: PASS

Test-02: PASS

The code goes as such:

$command = "(diff call on 2 files)";
my @output = `$command`;
print "Test-02: ";
$toPrint = "PASS";
foreach my $x (@output) {
    if ($x =~ /./) {
        $toPrint = "FAIL";
    }
}

This is a quick hackery job to fail if there is any output from the diff call. Is there a way to check for exceptions thrown by the command called in the backticks?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

定格我的天空 2024-07-25 05:35:09

假设 diff 错误出现在 STDERR 上,如果您希望能够检查或记录错误,我推荐使用 CPAN 模块 Capture::Tiny:

use Capture::Tiny 'capture';
my ($out, $err) = capture { system($command) };

这就像反引号,但分别为您提供 STDOUT 和 STDERR。

Assuming that diff errors wind up on STDERR, if you'd like to be able to examine or log the errors, I recommend the CPAN module Capture::Tiny:

use Capture::Tiny 'capture';
my ($out, $err) = capture { system($command) };

That's like backticks, but gives you STDOUT and STDERR separately.

终止放荡 2024-07-25 05:35:09

检查 perlvar 中的 $?。 如果设置为 0,则没有信号,程序的返回码也为零。 这可能就是你想要的。

在这种情况下,您甚至可以只使用 system 并检查其返回值是否为零,同时将 stdout 和 stderr 重定向到 /dev/null。

Check perlvar for $?. If it's set to 0, there were no signals, and the return code from the program is also zero. That's probably what you want.

In this case, you could even just use system and check its return value for being zero, while redirecting the stdout and stderr to /dev/null.

你在我安 2024-07-25 05:35:09

perldoc 网站。 您必须向下滚动到或搜索“qx/STRING/”(不带引号)

There is a list of interesting ways to work with the output of a backticks-enclosed command at the perldoc site. You'll have to scroll down to or search for "qx/STRING/" (without the quotes)

绳情 2024-07-25 05:35:09

你也可以:

my @output = `$command 2>\&1`;

You can also:

my @output = `$command 2>\&1`;
橘和柠 2024-07-25 05:35:09

您还可以使用“diff -d”的输出进行运行,这将使您的代码更易于阅读。

foreach (`diff -d $args`){
  if (/^Only in/){
     do_whatever();
  }
}

You could also make a run through with the output of 'diff -d' which will make your code easier to read.

foreach (`diff -d $args`){
  if (/^Only in/){
     do_whatever();
  }
}
葮薆情 2024-07-25 05:35:08

perlfaq8:如何从外部命令捕获 STDERR? 中有答案,


共有三个运行外部命令的基本方法:

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

使用 system(),STDOUT 和 STDERR 都将转到与脚本的 STDOUT 和 STDERR 相同的位置,除非 system() 命令重定向它们。 反引号和 open() 仅读取命令的 STDOUT。

您还可以使用 IPC::Open3 中的 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);

读取命令的 STDERR: 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> ) {}

但是实际上没有必要将两者都作为临时文件...以下应该可以工作同样,没有死锁:

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> ) {}

而且它也会更快,因为我们可以立即开始处理程序的标准输出,而不是等待程序完成。

使用其中任何一个,您都可以在调用之前更改文件描述符:

open(STDOUT, ">logfile");
system("ls");

或者您可以使用 Bourne shell 文件描述符重定向:

$output = `$cmd 2>some_file`;
open (PIPE, "cmd 2>some_file |");

您还可以使用文件描述符重定向使 STDERR 成为 STDOUT 的副本:

$output = `$cmd 2>&1`;
open (PIPE, "cmd 2>&1 |");

请注意,您不能简单地打开 STDERR在 Perl 程序中复制 STDOUT 并避免调用 shell 来执行重定向。 这不起作用:

open(STDERR, ">&STDOUT");
$alloutput = `cmd args`;  # stderr still escapes

这会失败,因为 open() 使 STDERR 转到 open() 时 STDOUT 所在的位置。 然后反引号使 STDOUT 转到字符串,但不更改 STDERR(它仍然转到旧的 STDOUT)。

请注意,您必须在反引号中使用 Bourne shell (sh(1)) 重定向语法,而不是 csh(1)! 有关为什么 Perl 的 system() 以及反引号和管道打开都使用 Bourne shell 的详细信息,请参阅 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");

在所有这些示例中,排序都很重要。 这是因为 shell 严格按照从左到右的顺序处理文件描述符重定向。

system("prog args 1>tmpfile 2>&1");
system("prog args 2>&1 1>tmpfile");

第一个命令将标准输出和标准错误发送到临时文件。 第二个命令仅发送旧的标准输出,并且旧的标准错误显示在旧的标准输出上。

There's the answer in perlfaq8: How can I capture STDERR from an external command?


There are three basic ways of running external commands:

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

With system(), both STDOUT and STDERR will go the same place as the script's STDOUT and STDERR, unless the system() command redirects them. Backticks and open() read only the STDOUT of your command.

You can also use the open3() function from IPC::Open3. Benjamin Goldberg provides some sample code:

To capture a program's STDOUT, but discard its 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);

To capture a program's STDERR, but discard its 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);

To capture a program's STDERR, and let its STDOUT go to our own STDERR:

use IPC::Open3;
use Symbol qw(gensym);
my $pid = open3(gensym, ">&STDERR", \*PH, "cmd");
while( <PH> ) { }
waitpid($pid, 0);

To read both a command's STDOUT and its STDERR separately, you can redirect them to temp files, let the command run, then read the temp files:

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> ) {}

But there's no real need for both to be tempfiles... the following should work just as well, without deadlocking:

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> ) {}

And it'll be faster, too, since we can begin processing the program's stdout immediately, rather than waiting for the program to finish.

With any of these, you can change file descriptors before the call:

open(STDOUT, ">logfile");
system("ls");

or you can use Bourne shell file-descriptor redirection:

$output = `$cmd 2>some_file`;
open (PIPE, "cmd 2>some_file |");

You can also use file-descriptor redirection to make STDERR a duplicate of STDOUT:

$output = `$cmd 2>&1`;
open (PIPE, "cmd 2>&1 |");

Note that you cannot simply open STDERR to be a dup of STDOUT in your Perl program and avoid calling the shell to do the redirection. This doesn't work:

open(STDERR, ">&STDOUT");
$alloutput = `cmd args`;  # stderr still escapes

This fails because the open() makes STDERR go to where STDOUT was going at the time of the open(). The backticks then make STDOUT go to a string, but don't change STDERR (which still goes to the old STDOUT).

Note that you must use Bourne shell (sh(1)) redirection syntax in backticks, not csh(1)! Details on why Perl's system() and backtick and pipe opens all use the Bourne shell are in the versus/csh.whynot article in the "Far More Than You Ever Wanted To Know" collection in http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz . To capture a command's STDERR and STDOUT together:

$output = `cmd 2>&1`;                       # either with backticks
$pid = open(PH, "cmd 2>&1 |");              # or with an open pipe
while (<PH>) { }                            #    plus a read

To capture a command's STDOUT but discard its 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

To capture a command's STDERR but discard its 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

To exchange a command's STDOUT and STDERR in order to capture the STDERR but leave its STDOUT to come out our old 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

To read both a command's STDOUT and its STDERR separately, it's easiest to redirect them separately to files, and then read from those files when the program is done:

system("program args 1>program.stdout 2>program.stderr");

Ordering is important in all these examples. That's because the shell processes file descriptor redirections in strictly left to right order.

system("prog args 1>tmpfile 2>&1");
system("prog args 2>&1 1>tmpfile");

The first command sends both standard out and standard error to the temporary file. The second command sends only the old standard output there, and the old standard error shows up on the old standard out.

转角预定愛 2024-07-25 05:35:08

程序本身不能抛出“异常”,但它们可以返回非零错误代码。 您可以使用 $? 检查 Perl 中使用反引号或 system() 运行的程序的错误代码:(在

$toPrint = "FAIL" if $?;

测试 @output 的循环之前添加此行。)

Programs themselves can't throw "exceptions", but they can return nonzero error codes. You can check the error code of a program run with backticks or system() in Perl using $?:

$toPrint = "FAIL" if $?;

(Add this line before the loop that tests @output.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文