r - 在 perl 中打开管道时直接使用 STDERR

标签 r perl pipe execution

我正在使用 open( my $command_out, "-|", $command_string ) 来执行命令并即时处理其输出(不必先等待命令完成,如 system()).

我注意到,当我以这种方式调用一些 R 脚本时,一些 R 消息会打印到屏幕上(例如 Loading required package: ...)。我猜这是因为 R 将此输出发送到 stderr(?尽管这些并不是真正的错误)。

是否可以在 open() 时将此输出也定向到 $command_out,以便屏幕保持干净?

最佳答案

假设你的 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

From here, you could discard the standard error or merge it with another stream.

Discarding the standard error

One way to discard the standard error is to use a shell redirect of 2>/dev/null. This is a general mechanism, and 2 is the standard error's file descriptor. For example:

#! /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

But sometimes you don't want the shell putting its grubby mitts on your command. Maybe it contains shell metacharacters or quotes that you don't want to deal with escaping. This is when a safe pipe-open is useful:

#! /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 一个子句柄并将子句柄的标准输出连接到该句柄。喜欢fork ,它向子进程返回 0,向父进程返回一个非零进程标识符,或者在失败时返回未定义的值。

在 child 中,我们首先将STDERR重定向到/dev/null,然后使用exec用我们的 R 程序替换 child 。请注意,我们以列表形式传递命令以绕过 shell:

If there is more than one argument in LIST, or if LIST is an array with more than one value, calls execvp(3) with the arguments in LIST.

因为我们再也看不到标准错误了,所以显式关闭 $command_out 以检查 child 是否愉快地运行很重要。否则,您会遇到令人费解的静默失败。

样本运行:

$ ./prog.pl 
got: foo & bar

Merge STDERR into STDOUT

To see the standard error on your handle, use 2>&1 instead, e.g.,

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文档涵盖了这一点:

You may also, in the Bourne shell tradition, specify an EXPR beginning with >&, in which case the rest of the string is interpreted as the name of a filehandle (or file descriptor, if numeric) to be duped (as dup(2)) and opened.

即使您可以通过这种方式看到标准错误,使用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

关于r - 在 perl 中打开管道时直接使用 STDERR,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3486575/

相关文章:

r - 如何将引号应用于函数中的每个参数

r - 快速读取非常大的表作为数据框

arrays - 将数组分配给哈希子键

java - 捕获时 ffmpeg 管道阻塞

r - 使用 read_excel 上传并自动命名所有现有工作簿工作表(不指定工作表数量)

perl - 如何在Perl中制作 “final”方法?

perl - 如何在 Perl 中指定 "tail slice"?

c - 如何在我的程序中使用终端命令的输出?

c - 即使添加了 #include <stdio.h> 也隐式声明了 popen

r - dplyr:在数据帧中用 n 次连续重复整数改变新列