在perl中打开管道时直接STDERR

Dav*_*d B 2 perl r pipe execution

open( my $command_out, "-|", $command_string )用来执行一个命令并动态处理它的输出(不必等待命令先完成,如同system()).

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

是否有可能太直接了这个输出$command_outopen()-ing所以屏幕将保持清洁?

Gre*_*con 10

说你的R程序是

#! /usr/bin/env r

require(Hmisc)

cat(argv[1], "\n")
Run Code Online (Sandbox Code Playgroud)

结果令人惊讶的是:

$ ./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

从这里,您可以丢弃标准错误或将其与另一个流合并.

丢弃标准错误

丢弃标准错误的一种方法是使用shell重定向2>/dev/null.这是一种通用机制,2是标准错误的文件描述符.例如:

#! /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: $_";
}
Run Code Online (Sandbox Code Playgroud)

shell也会处理反引号或qx//表达式

#! /usr/bin/perl

use warnings;
use strict;

(my $command_out = `./prog.r foo 2>/dev/null`) =~ s/^/got: /mg;
print $command_out;
Run Code Online (Sandbox Code Playgroud)

并传递一个标量命令 system

#! /usr/bin/perl

use warnings;
use strict;

system("./prog.r foo 2>/dev/null") == 0
  or warn "$0: prog.r exited " . ($? >>8);
Run Code Online (Sandbox Code Playgroud)

对于所有这些,输出是

$ ./prog.pl 
got: foo

但有时你希望shell把你的肮脏手套放在你的命令上.也许它包含你不想处理转义的shell元字符或引号.这是一个安全的管道打开很有用:

#! /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);
Run Code Online (Sandbox Code Playgroud)

打开"-|"叉子上的手柄并将孩子的标准输出连接到该手柄.例如fork,它向子节点返回0,向父节点返回非零进程标识符,或者在失败时返回未定义的值.

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

如果LIST中有多个参数,或者LIST是一个具有多个值的数组,则使用LIST中的参数调用execvp(3).

因为我们不能再看到标准错误,所以明确close $command_out地检查孩子是否幸福地运行是很重要的.否则,你会得到一个令人费解的无声失败.

样品运行:

$ ./prog.pl 
got: foo & bar

合并STDERRSTDOUT

要查看句柄上的标准错误,请2>&1改用,例如,

open my $command_out, "-|", "./prog.r foo 2>&1" or die;
Run Code Online (Sandbox Code Playgroud)

使用安全管道打开,dup标准输出上的标准错误:

if ($pid == 0) {
  # child
  open STDERR, ">&", \*STDOUT  or die "$0: open: $!";
  exec "./prog.r", "foo & bar" or die "$0: exec: $!";
}
Run Code Online (Sandbox Code Playgroud)

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


Wil*_*ell 5

一种简单的方法是将 2>&1 附加到 $command_string,但这是否有效取决于您的 shell。(例如,解释 $command_string 的 shell)