如何使用PerlIPC::Run3从子进程读取stdout和stderr



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

我遇到了IPC::Run3,我已经让它工作了,但我对文件句柄的使用很糟糕——基本上,我必须生成一个cat子进程来获得一个句柄,我可以告诉IPC::Run3向其写入,这样我的脚本就可以从中读取,而我为此传递STDIN的尝试失败了。我做错了什么?

#!/usr/bin/perl
use strict;
use warnings;
use IPC::Run3;
#my $pipe = *STDIN;           #-- this produces no output and hangs
#open(my $pipe, "<&STDIN");    #-- this outputs "foo bar" and hangs
open(my $pipe, "|cat");        #-- this works, but extra process is ugly
run3 "echo foo; echo bar >&2", undef, $pipe, $pipe;
while (<$pipe>) {
print ">>> $_";
}

您没有。改为使用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', '>&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, undef, '>pipe', (my $pipe = gensym), '2>&1';
while (<$pipe>) {
...
}
close($pipe);

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

use IPC::Run qw( run );
run @cmd, undef, 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"], undef, '>pipe', (my $pipe = gensym), '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", "-c", "echo foo; echo bar >&2"], undef, '>pipe', (my $pipe = new IO::Handle), '2>&1';
while (<$pipe>) {
print ">>> $_";
}

IPC::Run3可以在创建一个IO::Pipe并传递"作家;文件句柄,但这涉及到分叉你的主进程,所以我不推荐它,尽管它确实回答了我最初的问题@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", undef, $pipe, $pipe;
}
else {
# fork() failed
die "failed to fork - $!";
}

最新更新