如何在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将与脚本的STDOUT
和STDERR,
位于同一位置。 反引号和open()
只读你的命令的STDOUT
。
你也可以打开类似于下面的内容来redirectSTDOUT
和STDERR
。
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);