57

是否可以从 Perl 运行外部进程,捕获其标准错误、标准输出和进程退出代码?

我似乎能够将这些组合起来,例如使用反引号来获取标准输出,使用 IPC::Open3 来捕获输出,以及使用 system() 来获取退出代码。

如何一次捕获标准错误、标准输出和退出代码?

4

6 回答 6

42

更新:我更新了 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 输出。

于 2008-09-20T22:48:35.507 回答
27

如果您重新阅读 IPC::Open3 的文档,您会看到一条说明,您应该调用waitpid来获取子进程。完成此操作后,状态应该在$?. 退出值为$? >> 8。请参阅 $?perldoc perlvar

于 2008-09-20T19:49:11.913 回答
14

如果您不想要 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 位退出值。如果命令无法启动、死于信号或返回意外的退出值,它也会引发详细的异常。这意味着对于许多程序,您可以依靠 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提供了一个干净简单的界面来重新检查所有三个常见的文件句柄,但不幸的是它不会检查命令是否成功,所以你需要检查 $? 手动,这一点也不好玩。提供一个公共接口来检查 $? 自从检查 $ ? 以跨平台的方式不是我希望任何人完成的任务。

IPC::命名空间中的其他模块也可以为您提供帮助。YMMV。

祝一切顺利,

保罗

于 2008-09-21T00:57:54.980 回答
7

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

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

使用system(),STDOUT和 STDERR 将与脚本的位置相同STDOUTSTDERR,除非system()命令重定向它们。反引号并open()只读STDOUT您的命令。

您还可以使用 open 调用类似以下内容来重定向STDOUTSTDERR

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

$?@Michael Carman所述,返回码始终存储在其中。

于 2008-09-20T19:53:28.067 回答
0

如果你变得非常复杂,你可能想试试 Expect.pm。但是,如果您也不需要管理向进程发送输入,那可能就有点过头了。

于 2008-09-21T03:15:38.300 回答
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);
于 2012-04-25T17:10:27.953 回答