微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用 Perl IPC::Run3 从子进程中读取 stdout 和 stderr?

如何解决如何使用 Perl IPC::Run3 从子进程中读取 stdout 和 stderr?

我想从 Perl 脚本运行 make 命令,以便我可以捕获它的 stdout 和 stderr 流。我知道我可以使用 open(MAKE,"make 2>&1 |") 但这会给构建正确的 shell 命令以将参数传递给 make 带来问题,并且使用 open(MAKE,"-|",@makecmd,"2>&1") 不起作用,因为将命令作为数组传递不起作用生成一个子shell来进行重定向

我遇到了 IPC::Run3 并且我已经让它工作了,但是我对文件句柄的使用很丑 - 基本上我不得不产生一个 cat 子进程来获得一个我可以告诉的句柄 { {1}} 写入以便我的脚本可以从中读取,我为此目的传递 IPC::Run3 的尝试失败了。我做错了什么?

STDIN

解决方法

你没有。您可以改用 IPC::Run。天真地使用 IPC::Open3 会让你容易陷入死锁。避免这种情况涉及使用 IO::Select 或其他一些机制。涉及的工作是广泛的。 IPC::Open3 对于实际使用来说太低级了。


也就是说,您只处理一个文件句柄。 可以相对简单地使用 open3 来完成。

use IPC::Open3 qw( open3 );

open(local *CHILD_STDIN,'<','/dev/null') or die $!;
*CHILD_STDIN if 0;
pipe(local (*READER,*WRITER)) or die $!;
my $pid = open3('<&CHILD_STDIN','>&WRITER',@cmd);

close(WRITER);

while (<READER>) {
   ...
}

waitpid($pid);

呸!使用 IPC::Run 更干净。

use IPC::Run qw( run );

run \@cmd,\undef,'>pipe',\my $pipe,'2>&1';

while (<$pipe>) {
   ...
}

close($pipe);

嗯,这就是文档所说的,但它不起作用。你确实需要

use IPC::Run qw( run );
use Symbol   qw( gensym );

run \@cmd,(my $pipe = gensym),'2>&1';

while (<$pipe>) {
   ...
}

close($pipe);

如果你想要所有的输出,你可以简单地使用

use IPC::Run qw( run );

run \@cmd,\my $output;

最后,您提到了构建 shell 命令的问题。

您要找的是

use String::ShellQuote qw( shell_quote );

my $cmd = shell_quote(@cmd) . ' 2>&1';
open(my $pipe,'-|',$cmd);

while (<$pipe>) {
   ...
}

close($pipe);
,

感谢@ikegami,我决定了这个......

#!/usr/bin/perl

use strict;
use warnings;
use IPC::Run qw(run);       # CPAN or yum install perl-IPC-Run
use Symbol qw(gensym);

run ["sh","-c","echo foo; echo bar >&2"],'2>&1';

while (<$pipe>) {
    print ">>> $_";
}

或者,如果 gensym 对您来说有点神秘...

#!/usr/bin/perl

use strict;
use warnings;
use IPC::Run qw(run);       # CPAN or yum install perl-IPC-Run
use IO::Handle;

run ["sh",(my $pipe = new IO::Handle),'2>&1';

while (<$pipe>) {
    print ">>> $_";
}
,

IPC::Run3 如果您创建一个 IO::Pipe 并将“writer”文件句柄传递给它,则可以使用它,但这涉及分叉您的主进程,因此我不推荐它,尽管它确实回答了我的问题原始问题。 @ikegami 使用 IPC::Run 的解决方案更加优雅。

#!/usr/bin/perl

use strict;
use warnings;
use IPC::Run3;              # CPAN or yum install perl-IPC-Run3
use IO::Pipe;

my $pipe = new IO::Pipe;

if(my $pid = fork()) {
    # Parent
    $pipe->reader();

    while (<$pipe>) {
        print ">>> $_";
    }
}
elsif (defined $pid) {
    # Child ($pid = 0)
    $pipe->writer();
    run3 "echo foo; echo bar >&2",$pipe,$pipe;
}
else {
    # fork() failed
    die "failed to fork - $!";
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。