【问题标题】:Is there a way to capture a subroutine's print output to a variable so I can send it to stderr instead?有没有办法将子例程的打印输出捕获到变量中,以便我可以将其发送到 stderr?
【发布时间】:2013-05-12 23:44:40
【问题描述】:

假设我们有:

sub test {
        print "testing\n";
}

如果我想让它打印到 stderr 而不是 stdout,有没有办法可以调用子例程来执行此操作?或者我可以将输出捕获到变量然后使用警告吗?我对 perl 还很陌生。

【问题讨论】:

    标签: perl stderr


    【解决方案1】:

    是的,有。 print 将其输出发送到“选定”文件句柄,通常是 STDOUT。但是 Perl 提供了select 函数供您更改。

    select(STDERR);
    &test;           # send output to STDERR
    select(STDOUT);  # restore default output handle
    

    select 函数返回之前选择的文件句柄,因此您可以捕获它并在以后恢复它。

    my $orig_select = select(STDERR);
    &test;
    select($orig_select);
    

    【讨论】:

    • +1 请注意,test() 中的致命异常会留下非标准的 FILEHANDLE select()d,这就是为什么我在下面的答案中使用 local()ize。
    【解决方案2】:

    Perl 通过local() 的动态作用域并不经常使用,但我觉得这是一个很好的应用程序:

    test(); # to stdout
    {
        open(local *STDOUT, ">&STDERR") or die "dup out to err: $!";
        test(); # to stderr, locally calling it "STDOUT"
    }
    test(); # to stdout again
    

    在上面的块中对test() 的调用——以及对test() 本身可能调用的任何东西——的STDOUT 将动态限定为您的STDERR 副本。当控制离开块时,即使通过die()ing,STDOUT 也会恢复到块之前的状态

    广义的:

    sub out2err(&) {
      my $user_block = shift;
      open(local *STDOUT, ">&STDERR") or die $!;
      $user_block->();
    }
    
    test();             # to stdout
    out2err { test() }; # to stderr
    test();             # to stdout
    

    【讨论】:

    • +1 即使给定函数显式打印到STDOUT,这也会起作用。
    【解决方案3】:

    同时,您还可以“将子程序的打印输出捕获到变量中”。

    只需将标量引用传递给open

    #! /usr/bin/env perl
    use common::sense;
    use autodie;
    
    sub tostring (&) {
      my $s;
      open local *STDOUT, '>', \$s;
      shift->();
      $s
    }
    
    sub fake {
      say 'lalala';
      say 'more stuff';
      say 1 + 1, ' = 2';
      say for @_;
    }
    
    for (tostring { fake(1, 2, 3) }) {
      s/\n/\\n/g;
      say "Captured as string: >>>$_<<<";
    }
    

    输出:

    Captured as string: >>>lalala\nmore stuff\n2 = 2\n1\n2\n3\n<<<
    

    【讨论】:

      【解决方案4】:

      这对我有用

      local *STDOUT;
      open(STDOUT, ">", \$Result);
      &test();
      print $Result;
      

      【讨论】:

      • 此代码将不起作用,并且不会打印任何内容,而不是 &test(); 打印的结果。以下代码将起作用:do { local *STDOUT; open(STDOUT, "&gt;", \$Result); &amp;test(); }; print $Result;
      猜你喜欢
      • 2023-04-07
      • 2011-04-13
      • 2015-05-04
      • 2012-08-29
      • 1970-01-01
      • 1970-01-01
      • 2013-03-29
      • 2018-06-29
      • 1970-01-01
      相关资源
      最近更新 更多