我想从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 - $!";
}