我使用open( my $command_out, "-|", $command_string )
执行命令并动态处理其输出(无需等待命令先完成,如system()
中所示)。
我注意到当我以这种方式调用一些R脚本时,一些R消息被打印到屏幕上(例如Loading required package: ...
)。我想这是因为R将此输出发送到stderr(?虽然这些不是真正的错误)。
是否可以在$command_out
时将此输出定向到open()
,以便屏幕保持干净?
答案 0 :(得分:10)
说你的R程序是
#! /usr/bin/env r
require(Hmisc)
cat(argv[1], "\n")
结果令人惊讶的是:
$ ./prog.r foo Loading required package: Hmisc Loading required package: methods Loading required package: survival Loading required package: stats Loading required package: utils Loading required package: graphics Loading required package: splines Attaching package: 'Hmisc' The following object(s) are masked from package:survival : untangle.specials The following object(s) are masked from package:base : format.pval, round.POSIXt, trunc.POSIXt, units foo
从此处,您可以放弃标准错误或将其与其他流合并。
丢弃标准错误的一种方法是使用2>/dev/null
的shell重定向。这是一种通用机制,2是standard error's file descriptor。例如:
#! /usr/bin/perl
use warnings;
use strict;
open my $command_out, "-|", "./prog.r foo 2>/dev/null"
or die "$0: could not start prog.r";
while (<$command_out>) {
print "got: $_";
}
shell还将处理backtick or qx//
个表达式
#! /usr/bin/perl
use warnings;
use strict;
(my $command_out = `./prog.r foo 2>/dev/null`) =~ s/^/got: /mg;
print $command_out;
并将标量命令传递给system
#! /usr/bin/perl
use warnings;
use strict;
system("./prog.r foo 2>/dev/null") == 0
or warn "$0: prog.r exited " . ($? >>8);
对于所有这些,输出是
$ ./prog.pl got: foo
但有时你不希望shell将你的肮脏手套放在你的命令上。也许它包含你不想处理转义的shell元字符或引号。这是safe pipe-open有用的时候:
#! /usr/bin/perl
use warnings;
use strict;
my $pid = open my $command_out, "-|";
die "$0: fork: $!" unless defined $pid;
if ($pid == 0) {
# child
open STDERR, ">", "/dev/null" or die "$0: open: $!";
exec "./prog.r", "foo & bar" or exit 1; # STDERR silent now
}
while (<$command_out>) {
print "got: $_";
}
close $command_out or warn "$0: prog.r exited " . ($? >> 8);
打开"-|"
上的句柄,分叉一个孩子并将孩子的标准输出连接到该句柄。与fork
类似,它向子节点返回0,向父节点返回非零进程标识符,或者在失败时返回未定义的值。
在孩子中,我们首先将STDERR
重定向到/dev/null
,然后使用exec
将孩子替换为我们的R计划。请注意,我们以列表形式传递命令以绕过shell:
如果LIST中有多个参数,或者LIST是一个具有多个值的数组,则使用LIST中的参数调用execvp(3)。
因为我们不能再看到标准错误了,所以明确close $command_out
检查孩子是否幸福地运行是很重要的。否则,你会得到一个令人费解的无声失败。
示例运行:
$ ./prog.pl got: foo & bar
STDERR
合并到STDOUT
要查看句柄上的标准错误,请使用2>&1
,例如,
open my $command_out, "-|", "./prog.r foo 2>&1" or die;
安全管道打开,dup
标准输出上的标准错误:
if ($pid == 0) {
# child
open STDERR, ">&", \*STDOUT or die "$0: open: $!";
exec "./prog.r", "foo & bar" or die "$0: exec: $!";
}
open
文档涵盖了这一点:
在Bourne shell传统中,您还可以指定以
>&
开头的EXPR,在这种情况下,字符串的其余部分将被解释为文件句柄(或文件描述符,如果是数字)的名称欺骗(dup(2))并打开。
即使您可以通过这种方式查看标准错误,但仍然可以使用close
检查孩子的退出状态。
现在所有到达$command_out
:
got: Loading required package: Hmisc got: Loading required package: methods got: Loading required package: survival got: Loading required package: stats got: Loading required package: utils got: Loading required package: graphics got: Loading required package: splines got: got: Attaching package: 'Hmisc' got: got: got: The following object(s) are masked from package:survival : got: got: untangle.specials got: got: got: The following object(s) are masked from package:base : got: got: format.pval, got: round.POSIXt, got: trunc.POSIXt, got: units got: got: foo & bar
答案 1 :(得分:4)
一种简单的方法是将2&gt;&amp; 1附加到$ command_string,但这是否有效取决于你的shell。 (例如,解释$ command_string的shell)
答案 2 :(得分:3)
使用IPC::Run
分别捕获STDOUT和STDERR。 pump
函数可以即时为您提供输出。
答案 3 :(得分:1)
也许你可以将STDERR重定向到整个程序的STDOUT?
*STDERR = *STDOUT;
open( my $command.... );
答案 4 :(得分:0)
自行分叉并执行:
pipe R, W; # note: R and W have CLOEXEC set
if (fork == 0) {
# child #
open STDERR, '>&W';
exec qw/ls -lrtac/, $ARGV[0];
die 'not reached';
}
# parent #
close W;
while (<R>) { do_something }
close R;
wait;
}