有没有一种方法可以将子程序的打印输出捕获到一个变量中,以便我可以将其发送到stderr?

8
假设我们有以下内容:

假设我们有:

sub test {
        print "testing\n";
}

如果需要将输出打印到stderr而不是stdout,我该如何调用子例程呢?或者,我可以将输出捕获到变量中,然后使用warn吗?我对perl还相当新手。
4个回答

12

有的。 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);

请注意,test() 中的致命异常将使非标准 FILEHANDLE 被 select(),这就是为什么我在下面的答案中使用 local()ize 的原因。+1 - pilcrow

9

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(),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
+1 这将起作用,即使给定的函数明确地打印到 STDOUT - chepner

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<<<

1
这对我有用。
local *STDOUT;
open(STDOUT, ">", \$Result);
&test();
print $Result;

这段代码无法正常工作,不会打印出 &test() 打印的结果,而是什么也不会打印。下面的代码可以正常工作:do { local *STDOUT; open(STDOUT, ">", \$Result); &test(); }; print $Result; - Eric Klien

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接