2008-09-20 93 views
56

是否有可能从Perl运行外部进程,捕获它的stderr,stdout和进程退出代码?如何在Perl中一次性捕获stderr,stdout和退出代码?

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

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

回答

26

如果您重新阅读了IPC :: Open3的文档,您会看到一条注释,您应该致电waitpid收获子进程。一旦你这样做了,状态应该在$?。退出值为$? >> 8。见 $? in perldoc perlvar

+1

我不知道为什么这些链接被搞砸了。在编辑/预览中一切都很好。 – 2008-09-20 19:57:39

+2

我已经发送给perl5porters一个补丁到IPC :: Open2和:: Open3来显示这些模块的概要中的waitpid。 :) – 2008-09-21 21:56:25

7

有执行外部指令的三种基本方法:

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

随着system(),既STDOUT和STDERR会去同一个地方脚本的STDOUTSTDERR,除非system()命令会将它们。反引号和open()只读你的命令的STDOUT

您也可以打开以下内容,打开以重定向STDOUTSTDERR

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

返回代码总是存储在$?@Michael Carman指出。

+0

不要忘记`qx //` – 2017-04-06 07:36:07

41

更新:我更新的IO :: CaptureOutput的API,使这个更容易。)

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

use IO::CaptureOutput qw/capture_exec/; 

my ($stdout, $stderr, $success, $exit_code) = capture_exec(@cmd); 

这是capture_exec()函数,但是IO :: CaptureOutput还具有可以被用来捕捉是Perl输出或输出从更一般的捕获()函数外部程序。所以如果一些Perl模块碰巧使用一些外部程序,你仍然会得到输出。

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

+12

它似乎[Capture :: Tiny](http://search.cpan.org/dist/Capture-Tiny/)更新更好:'这个模块受到启发通过IO :: CaptureOutput,它提供了类似的功能,但不具备输出和更复杂的代码和API的功能。 IO :: CaptureOutput不处理图层或“限制”一节中描述的大多数异常情况,我不再推荐它。 - http://search.cpan.org/~dagolden/Capture-Tiny-0.18/lib/Capture/Tiny.pm#SEE_ALSO – 2012-07-31 20:18:30

14

如果你不希望STDERR的内容,然后从IPC::System::Simple模块捕获()命令是几乎完全你追求的:

use IPC::System::Simple qw(capture system $EXITVAL); 

    my $output = capture($cmd, @args); 

    my $exit_value = $EXITVAL; 

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

与Perl的内置系统和反引号命令不同,IPC :: System :: Simple在Windows下返回完整的32位退出值。如果命令无法启动,死于信号或返回意外的退出值,它也会引发一个详细的异常。这意味着许多程序,而不是检查出口值自己,你可以依靠 IPC ::系统::简单为你做的辛勤工作:

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 ::系统::简单是纯Perl,没有依赖关系,可以在Unix和Windows系统上运行。不幸的是,它不提供捕获STDERR的方法,因此可能不适合您的所有需求。

IPC::Run3提供了一个干净而简单的界面,用于重新配置所有三个常用文件句柄,但不幸的是,它不检查命令是否成功,因此您需要检查$?手动,这是不是很有趣。提供检查$的公共接口?是在我的to-do list上IPC :: System :: Simple的东西,因为检查$?以跨平台的方式不是我希望任何人的任务。

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

一切顺利,

保罗

0

如果你变得非常复杂,你可能会想尝试Expect.pm。但是,如果你不需要管理向流程发送输入,那也可能是过度的。

0

我发现IPC:run3会非常有帮助。您可以将所有子管道转发给glob或变量;非常简单地!退出代码将存储在$?中。

下面是我如何抓住stderr,我知道这将是一个数字。 cmd输出信息转换为标准输出(我使用>输入到args中的文件)并报告了多少转换为STDERR。

use IPC::Run3 

my $number; 
my $run = run3("cmd arg1 arg2 >output_file",\undef, \undef, \$number); 
die "Command failed: $!" unless ($run && $? == 0); 
相关问题