【问题标题】:STDOUT redirected to variable not catching pipe outputSTDOUT 重定向到变量未捕获管道输出
【发布时间】:2018-04-13 04:48:33
【问题描述】:

我想临时将标准输出重定向到内存变量。打印正确地重定向到我的变量,但不是管道的输出(在我的示例中为 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

【问题讨论】:

  • bc 被分叉并作为具有独立 STDOUT 的单独进程运行。您需要使用类似IPC::Run3 的方式运行它来捕获结果。

标签: perl


【解决方案1】:

这……不可能。

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

在此示例中,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

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-08
      • 2012-02-26
      • 1970-01-01
      • 2022-01-01
      • 1970-01-01
      • 2015-06-21
      • 2016-09-04
      • 1970-01-01
      相关资源
      最近更新 更多