STDOUT重定向到变量不捕获管道输出

时间:2017-10-31 22:04:52

标签: perl

我想暂时将stdout重定向到内存变量中。打印正确地重定向到我的变量但不是管道的输出(在我的示例中为bc)。到底是怎么回事?

#!/usr/bin/perl

my $stdout_sink;
open(my $orig_stdout, ">&STDOUT") || die $!;
close STDOUT;
open(STDOUT, ">", \$stdout_sink) || die $!;

# produce some output in different ways
print "before bc\n"; # ok
open my $fh, "| bc";
print $fh "2+2\n";   # not ok
close $fh;

close STDOUT;  
open(STDOUT, ">&", $orig_stdout) || die $!;
print "$stdout_sink";

实际输出将是:

before bc

预期产出:

before bc
4

2 个答案:

答案 0 :(得分:3)

您在mob's answer中详细说明了为什么您无法将孩子的STDOUT重定向到变量,而该变量并非真正的文件句柄。

相反,您可以使用模块运行外部程序,可以将标准流重定向到变量。然后,您可以根据需要将字符串与重定向输出组合在一起。

IPC::Run3

的示例
use warnings;
use strict;
use feature 'say';

use IPC::Run3;

open my $fh, ">", \my $so_1;     
my $old = select $fh;         # make $fh the default for output,
say "To select-ed default";   # so prints end up in $so_1

run3 ["ls", "-l", "./"], undef, \my $so_2;   # output goes to $so_2

select $old;                  # restore STDOUT as default for output
print $so_1, $so_2;

在这里,我使用select来操作默认打印的位置(未指定文件句柄)。

请注意,该示例将run3重定向到与用于上一个重定向的变量($so_2)不同的变量。如果您要添加到同一个变量,请在%options

中指定
run3 ["ls", "-l", "./"], undef, \$so_1, { append_stdout => 1 };

并从打印声明中删除$so_2

该模块使用临时文件进行此重定向,正如小组在答案中所示。

其他一些选项Capture::Tiny可以从几乎任何代码重定向输出,界面简洁干净,非常强大,圆润,复杂IPC::Run

答案 1 :(得分:2)

这是......不可能。

管道打开的标准输出和system调用被写入文件描述符1.通常,Perl的STDOUT文件句柄与文件描述符1相关联,但可以对其进行操作。 / p>

在此示例中,system调用写入STDOUT文件句柄,写入文件foo

close STDOUT;             # closes file descriptor 1
open STDOUT, '>', 'foo';  # reopens STDOUT as file descriptor 1
system("echo bar");
close STDOUT;
print STDERR "foo: ",`cat foo`;
# result:  "foo: bar"

但在此示例中,system调用写入BAZ文件句柄。

close STDOUT;             # closes file descriptor 1
open BAZ, '>', 'baz';     # attaches fd 1 to handle BAZ
open STDOUT, '>', 'foo';  # opens new file descriptor, maybe fd 3
system("echo bar");
close STDOUT;
print STDERR "foo: ",`cat foo`;
print STDERR "baz: ",`cat baz`;
# result:  "foo: baz: bar"

内存中的文件句柄不是真正的文件句柄。如果你打电话给fileno,你(通常可能依赖于操作系统)会得到一个负数。

open STDOUT, '>', \$scalar;
print STDERR fileno(STDOUT);     #   -1

管道打开,系统调用将无法写入此文件句柄。

您需要更复杂的解决方法,例如将管道打开输出写入文件,然后将该文件复制到内存中变量。