如何将标准输出和标准错误重定向到一个变量中

16

我想将STDERRSTDOUT重定向到一个变量中。我这样做了。

close(STDOUT);
close(STDERR);

my $out;
open(STDOUT, ">>", \$out);
open(STDERR, ">>", \$out);

for(1..10)
{
    print "print\n"; # this is ok. 
    warn "warn\n"; # same
    system("make"); # this is lost. neither in screen nor in variable.
}

使用system存在问题。我希望该调用的输出也能被捕获。

6个回答

14

这个答案可以解释如何使用核心Perl来完成,然后,也许可以提到一个现有的包来帮助复制代码。仅仅提供一个包的名称是一种不太好的解决方案,在我看来,即使它能工作。 - U. Windl

6

您是否想将输出内容保存到变量中?如果是这样,您需要使用反引号或qx{}并进行适当的重定向。例如,您可以使用:

#/usr/bin/env perl
use strict;
use warnings;

# Ensure we have a way to write messages
open my $fh, '>', "output" or die;

close(STDOUT);
close(STDERR);

my $out;
open(STDOUT, ">>", \$out) or do { print $fh, "failed to open STDOUT ($!)\n"; die };
open(STDERR, ">>", \$out) or do { print $fh, "failed to open STDERR ($!)\n"; die };

foreach my $i (1..10)
{
    print "print $i\n"; 
    warn "warn $i\n";
    my $extra = qx{make pth$i 2>&1};
    print $fh "<<$i>><<$out>><<$extra>>\n";
}

我恰好在目录中有程序pth1、pth2和pth3——它们都制作得很好;而pth4及以上则会将错误写入stderr,因此需要重定向。
您应该始终检查open()等操作的成功与否。
为什么这是必要的呢?因为向变量写入内容需要写入进程的协作,而make并不知道如何协作。

qx的问题在于你只能捕获一个(STDOUT)输出流。如果你想要单独获取STDERR,就不能将STDERR重定向到STDOUT。 - U. Windl

5

有几种方法可以重定向和恢复STDOUT。其中一些方法也适用于STDERR。以下是我最喜欢的两种方法:

使用select:

my $out;
open my $fh, ">>", \$out;
select $fh;
print "written to the variable\n";
select STDOUT;
print "written to original STDOUT\n";

使用 local:
my $out
do {
    local *STDOUT;
    open STDOUT, ">>", \$out;
    print "written to the variable\n";
};
print "written to original STDOUT\n";

享受。


看起来这个答案忽略了一个事实,即对于正在运行的外部进程,重定向到一个变量似乎会失败。 - U. Windl

2
这种情况发生的原因是 STDOUT 和 STDERR "文件句柄"与 shell 提供给 Perl 二进制文件的 stderr 和 stdout 句柄不相等。为了达到你想要的效果,你应该使用 open 而不是 system

2
@Israfil:这是唯一的方法。 - Jonathan Leffler
关于"你应该使用open而不是system":你应该解释一下opensystem之间的区别,或者准确说明你所指的open的具体变体。 - U. Windl

1
为什么不使用IPC::Open3?

那对于当前的进程是行不通的,只适用于新开启的进程。 - Jake

1

简短回答

use Capture::Tiny;

合并 STDOUT 和 STDERR

如果你想要将 print() 的输出和 warn() 的警告信息合并,那么可以使用...

my ($merged,  @result) = capture_merged { print "Hello, world!" };  # static code
my ($merged,  @result) = capture_merged { eval $codetoeval };       # code in variable

标准输出和标准错误分离

如果你想要它们分开...

my ($stdout, $stderr, @result) = capture { print "Hello, world!" };   # static code
my ($stdout, $stderr, @result) = capture { eval $codetoeval };        # code in variable

Eval结果

@result表示成功,成功的情况是[1],失败的情况是[]。Tiny还有很多其他函数,你可以查看其他情况,比如代码引用等等。但我认为上面的代码应该能满足大部分Perl开发者的需求。


这是在 https://dev59.com/1W855IYBdhLWcg3wUScW#4415743 之后的,但你解释了一些细节,所以我点赞了这个答案。 - U. Windl

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