是否可以从Perl运行外部进程,捕获其stderr,stdout和进程退出代码?

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

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

有帮助吗?

解决方案

如果你重读了IPC :: Open3的文档,你会看到一个你应该调用的注释 waitpid 收获子进程。完成此操作后,状态应该在 $?中可用。退出值是 $? >> 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 模块几乎就是你所追求的:

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

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

   my $exit_value = $EXITVAL;

您可以使用带有单个参数的capture()来调用shell,也可以使用多个参数来可靠地避免使用shell。还有capturex(),它永远不会调用shell,即使只有一个参数。

与Perl的内置系统和反引号命令不同,IPC :: System :: Simple在Windows下返回完整的32位退出值。如果命令无法启动,信号死亡或返回意外退出值,它也会抛出详细的异常。这意味着对于许多程序而言,您可以依赖,而不是自己检查退出值 IPC :: System ::简单易用:

 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的待办事项列表上的内容: :System :: Simple,因为检查$?跨平台的方式不是我希望任何人的任务。

IPC :: 命名空间中还有其他模块这也可能为您提供帮助。 YMMV。

一切顺利,

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

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

使用 system() STDOUT 和STDERR将与脚本的 STDOUT STDERR,除非 system()命令重定向它们。反引号和 open()只读取命令的 STDOUT

您还可以调用以下内容,打开以重定向 STDOUT STDERR

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

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

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

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

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

use IPC::Run3

my $number;
my $run = run3("cmd arg1 arg2 >output_file",\undef, \undef, \$number);
die "Command failed: $!" unless ($run && $? == 0);
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top