首页 文章

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

提问于
浏览
56

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

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

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

6 回答

  • 7

    如果您重读了IPC :: Open3的文档,您将看到一条注释,您应该调用waitpid来获取子进程 . 执行此操作后,状态应在 $? 中可用 . 退出值为 $? >> 8 . 见$? in perldoc perlvar .

  • 26

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

  • 41

    如果你不想要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 . 还有capturex(),它永远不会调用shell,即使只有一个参数 .

    与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提供了一个干净简单的界面来重新管理所有三个常见的文件句柄,但不幸的是它不需要检查$?手动,这一点都不好玩 . 提供检查$的公共界面?因为检查$ ?,我在to-do list上为IPC :: System :: Simple做了什么?跨平台的方式不是我希望任何人的任务 .

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

    祝一切顺利,

    保罗

  • 0

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

    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所述 .

  • 14

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

  • 0

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

    下面是我如何 grab 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);
    

相关问题