【问题标题】:Direct STDERR when opening pipe in perl在 perl 中打开管道时直接 STDERR
【发布时间】:2010-08-15 07:32:43
【问题描述】:

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

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

open()-ing 时,是否可以将此输出也定向到$command_out,以便屏幕保持清洁?

【问题讨论】:

    标签: r perl pipe execution


    【解决方案1】:

    假设你的 R 程序是

    #! /usr/bin/env r
    
    require(Hmisc)
    
    cat(argv[1], "\n")
    

    结果出乎意料地健谈:

    $ ./prog.r foo
    加载所需的包:Hmisc
    加载所需的包:方法
    加载所需包:survival
    加载所需的包:stats
    加载所需的包:utils
    加载所需包:图形
    加载所需的包:样条
    
    附件包:'Hmisc'
    
    
        以下对象从 package:survival 中屏蔽:
    
         untangle.specials
    
    
        以下对象从 package:base 中屏蔽:
    
         格式.pval,
         round.POSIXt,
         截断.POSIXt,
         单位
    
    富

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

    丢弃标准错误

    放弃标准错误的一种方法是使用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
    得到:foo

    但有时您希望外壳将其肮脏的手套放在您的命令上。也许它包含您不想处理转义的 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,向父进程返回非零进程标识符,或者在失败时返回未定义的值。

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

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

    因为我们再也看不到标准错误,所以显式地close $command_out 以检查孩子是否快乐地跑很重要。否则,你会得到一个令人费解的静默失败。

    示例运行:

    $ ./prog.pl
    得到:foo & bar

    STDERR 合并到STDOUT

    要查看句柄上的标准错误,请改用2&gt;&amp;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 传统中指定一个以 &gt;&amp; 开头的 EXPR,在这种情况下,字符串的其余部分被解释为要被欺骗的文件句柄(或文件描述符,如果是数字)的名称( dup(2)) 并打开。

    即使您可以通过这种方式查看标准错误,使用close 检查孩子的退出状态仍然是一个好主意。

    现在一切都到达$command_out

    得到:加载所需的包:Hmisc
    得到:加载所需的包:方法
    得到:加载所需的包:生存
    得到:加载所需的包:统计
    得到:加载所需的包:utils
    得到:加载所需的包:图形
    得到:加载所需的包:样条
    拿到:
    得到:附加包:'Hmisc'
    拿到:
    拿到:
    得到:以下对象从 package:survival 中屏蔽:
    拿到:
    得到: untangle.specials
    拿到:
    拿到:
    得到:以下对象从 package:base 中屏蔽:
    拿到:
    得到:format.pval,
    得到:round.POSIXt,
    得到:trunc.POSIXt,
    得到:单位
    拿到:
    得到:foo & bar

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

        使用IPC::Run 分别捕获STDOUT 和STDERR。 pump 函数为您提供即时输出。

        【讨论】:

          【解决方案4】:

          也许您可以将整个程序的 STDERR 重定向到 STDOUT?

          *STDERR = *STDOUT;
          open( my $command.... );
          

          【讨论】:

            【解决方案5】:

            自己做 fork 和 exec:

            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;
            

            }

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2020-04-20
              • 2013-07-13
              • 2021-09-05
              • 2013-07-16
              • 2023-04-05
              • 2020-01-07
              • 2011-12-26
              • 2022-12-11
              相关资源
              最近更新 更多