如何在Perl中捕获stderr,stdout和退出代码?

是否有可能从Perl运行外部进程,捕获它的stderr,stdout和进程退出代码?

我似乎能够做到这些的组合,例如使用反引号获取标准输出,IPC :: Open3捕获输出,和system()获得退出代码。

如何一次捕获stderr,stdout和退出代码?

如果您重新阅读了IPC :: Open3的文档,您会看到一条提示,您应该调用waitpid来获取subprocess。 一旦你这样做,状态应该在$? 。 退出值是$? >> 8 $? >> 8 。 看$? 在perldoc perlvar 。

更新 :我更新了IO :: CaptureOutput的API,使其更容易。)

有几种方法可以做到这一点。 这里有一个选项,使用IO :: CaptureOutput模块:

 use IO::CaptureOutput qw/capture_exec/; my ($stdout, $stderr, $success, $exit_code) = capture_exec( @cmd ); 

这是capture_exec()函数,但是IO :: CaptureOutput也有一个更通用的capture()函数,可以用来捕获Perl输出或者外部程序的输出。 所以如果一些Perl模块碰巧使用了一些外部程序,你仍然会得到输出。

这也意味着你只需要记住一个方法来捕获STDOUT和STDERR(或合并它们),而不是使用IPC :: Open3作为外部程序和其他捕获Perl输出的模块。

如果你不想要STDERR的内容,那么来自IPC :: System :: Simple模块的capture()命令几乎就是你所要做的:

  use IPC::System::Simple qw(capture system $EXITVAL); my $output = capture($cmd, @args); my $exit_value = $EXITVAL; 

您可以使用capture()以一个参数来调用shell或多个参数来可靠地避免shell。 还有一个从不调用shell的capturex(),即使只有一个参数。

与Perl的内置系统和反引号命令不同,IPC :: System :: Simple在Windows下返回完整的32位退出值。 如果命令无法启动,死于信号或返回意外的退出值,它也会抛出一个详细的exception。 这对许多程序来说意味着,而不是自己检查退出值,你可以依靠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提供了一个干净和简单的界面来重新pipe理所有三个常见的文件句柄,但不幸的是,它不检查命令是否成功,所以你需要检查$? 手动,这是不是很有趣。 提供检查$的公共接口? 是在IPC :: System :: Simple 的待办事项清单上 ,因为检查$? 在跨平台的时尚是不是我希望任何人的任务。

IPC ::命名空间中还有其他模块也可能为您提供帮助。 因人而异。

祝一切顺利,

保罗

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

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

使用system() ,除非system()命令redirect它们STDERR,否则STDOUT和STDERR将与脚本的STDOUTSTDERR,位于同一位置。 反引号和open()只读你的命令的STDOUT

你也可以打开类似于下面的内容来redirectSTDOUTSTDERR

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

返回码始终存储在$? 正如@Michael Carman所指出的那样 。

如果你真的很复杂,你可能想尝试Expect.pm。 但是,如果您不需要pipe理向stream程发送input,那也可能是过度的。

我发现IPC:run3是非常有帮助的。 您可以将所有子pipe道转发到一个glob或一个variables; 非常容易! 退出代码将被存储在$?中。

以下是我如何抓住stderr,我知道这将是一个数字。 cmd输出信息转换到标准输出(我通过>input到参数中的文件),并报告了多less转换到STDERR。

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