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

如何在Perl中读取外部命令的错误输出?,perl,backticks,Perl,Backticks,作为大型Perl程序的一部分,我将对照参考文件检查文件夹中输入文件的diff命令的输出,其中空白输出(匹配)是通过的结果,而diff的任何输出都是失败的结果 问题是,如果目标文件夹缺少预期的文件数,则异常diff throws不会作为输出出现,从而创建错误的传递 输出示例: diff: /testfolder/Test-02/test-output.2: No such file or directory 测试-01:通过 测试-02:通过 守则如下: $command = "(diff ca

作为大型Perl程序的一部分,我将对照参考文件检查文件夹中输入文件的
diff
命令的输出,其中空白输出(匹配)是通过的结果,而diff的任何输出都是失败的结果

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

输出示例:

diff: /testfolder/Test-02/test-output.2: No such file or directory
测试-01:通过

测试-02:通过

守则如下:

$command = "(diff call on 2 files)";
my @output = `$command`;
print "Test-02: ";
$toPrint = "PASS";
foreach my $x (@output) {
    if ($x =~ /./) {
        $toPrint = "FAIL";
    }
}
如果
diff
调用有任何输出,这是一个很快就会失败的黑客作业。是否有方法检查在
backticks
中调用的命令引发的异常?

检查。如果设置为0,则没有信号,程序返回的代码也为零。那可能是你想要的


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

程序本身不能抛出“异常”,但它们可以返回非零错误代码。您可以使用$?:

$toPrint = "FAIL" if $?;

(将这一行添加到测试
@output
的循环之前)

下面列出了一系列有趣的方法,可以使用backticks命令的输出。您必须向下滚动到或搜索“qx/STRING/”(不带引号)

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

foreach(`diff-d$args`){
如果(/^Only in/){
做什么都行;
}
}
您还可以:

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

假设差异错误最终出现在STDERR上,如果您希望能够检查或记录错误,我建议使用CPAN模块Capture::Tiny:

use Capture::Tiny 'capture';
my ($out, $err) = capture { system($command) };
这类似于backticks,但分别给出STDOUT和STDERR


运行外部命令有三种基本方式:

system $cmd;        # using system()
$output = `$cmd`;       # using backticks (``)
open (PIPE, "cmd |");   # using open()
使用system(),STDOUT和STDERR将与脚本的STDOUT和STDERR位于同一位置,除非system()命令重定向它们。Backticks和open()仅读取命令的标准输出

您还可以使用IPC::open3中的open3()函数。Benjamin Goldberg提供了一些示例代码:

要捕获程序的标准输出,但放弃其标准输出,请执行以下操作:

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);
$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
也可以使用Bourne shell文件描述符重定向:

$output = `$cmd 2>some_file`;
open (PIPE, "cmd 2>some_file |");
您还可以使用文件描述符重定向使STDERR成为STDOUT的副本:

$output = `$cmd 2>&1`;
open (PIPE, "cmd 2>&1 |");
请注意,您不能简单地在Perl程序中打开STDERR作为STDOUT的dup,并避免调用shell来执行重定向。这不起作用:

open(STDERR, ">&STDOUT");
$alloutput = `cmd args`;  # stderr still escapes
这会失败,因为open()使STDERR转到在open()时STDOUT要去的地方。然后,反勾号使STDOUT转到字符串,但不要更改STDERR(它仍然转到旧的STDOUT)

请注意,您必须在backticks中使用伯恩shell(sh(1))重定向语法,而不是csh(1)!关于Perl的system()和backtick以及pipe opens为什么都使用Bourne shell的详细信息,请参阅中“远比您想知道的多”集合中的VS/csh.whynot文章。要同时捕获命令的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
在所有这些示例中,排序都很重要。这是因为shell严格按照从左到右的顺序处理文件描述符重定向

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

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

+1。重定向到/dev/null也是一个好主意,不过使用-q选项进行diff会更快,因为它首先避免了生成大部分输出。添加了几乎完全相同的行,所有内容都立即进入正确的工作行为。
$output = `$cmd 2>&1`;
open (PIPE, "cmd 2>&1 |");
open(STDERR, ">&STDOUT");
$alloutput = `cmd args`;  # stderr still escapes
$output = `cmd 2>&1`;                       # either with backticks
$pid = open(PH, "cmd 2>&1 |");              # or with an open pipe
while (<PH>) { }                            #    plus a read
$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
$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
$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
system("program args 1>program.stdout 2>program.stderr");
system("prog args 1>tmpfile 2>&1");
system("prog args 2>&1 1>tmpfile");