Perl: How to pass IPC::Open3 redirected STDOUT/STDERR fhs

K.A*_*.B. 5 linux perl

I'm trying to capture the output my perl code generates both from print and similar statements and external commands.

Due to design constraints I can't use solutions like Capture::Tiny. I need to forward the output to the buffer variable as soon as it is generated and I need to be able to differentiate between STDOUT and STDERR. Ideally a solution for external commands would essentially work just like system apart from being able to capture STDOUT and STDERR instead of printing them.

My code is supposed to:

  1. Save the old STDOUT/STDERR file handles.
  2. Create a new ones for both STDERR and STDOUT.
  3. Redirect all the output to this place.
  4. Print a couple of things.
  5. Restore the old filehandles.
  6. Do something with the captured output, e.g. print it.

However I'm unable to capture the output generated from external commands. I can't do it with IPC::Run3 nor with IPC::Open3.

#!/usr/bin/perl -CSDAL
use warnings;
use strict;
use IPC::Open3;
#use IPC::Run3;

# Save old filehandles
open(my $oldout, ">&STDOUT") or die "Can't dup STDOUT: $!";
open(my $olderr, ">&STDERR") or die "Can't dup STDERR: $!";

my $buffer = "";

close(STDOUT);
close(STDERR);

open(STDOUT, '>', \$buffer) or die "Can't redirect STDOUT: $!";
*STDERR = *STDOUT; # In this example STDOUT and STDERR are printed to the same buffer.

print "1: Test\n";
#run3 ["date"], undef, \*STDOUT, \*STDERR; # This doesn't work as expected
my $pid = open3("<&STDIN", ">&STDOUT", ">&STDERR", "date");
waitpid($pid,0); # Nor does this.

print STDERR "2: Test\n";

open(STDOUT, ">&", $oldout) or die "Can't dup \$oldout: $!";
open(STDERR, ">&", $olderr) or die "Can't dup \$olderr: $!";

print "Restored!\n";
print $buffer;

Run Code Online (Sandbox Code Playgroud)

Expected result:

Restored!
1: Test
Mo 25. Mär 13:44:53 CET 2019
2: Test
Run Code Online (Sandbox Code Playgroud)

Actual result:

Restored!
1: Test
2: Test
Run Code Online (Sandbox Code Playgroud)

Håk*_*and -1

这还不是答案,但似乎在您调用时需要是常规的 tty 文件句柄open3,例如:STDOUTopen3

use feature qw(say);
use strict;
use warnings;

use IPC::Open3;
use Symbol 'gensym';
{
    local *STDOUT;  # <-- if you comment out this line open3 works as expected
    my ($chld_in, $chld_out);
    my $chld_err = gensym;
    my $pid;
    eval {
        $pid = open3($chld_in, $chld_out, $chld_err, "date");
    };
    if ( $@ ) {
        say "IPC::Open::open3 failed: '$@'";
    }
    print "-> $_" for <$chld_out>;
    waitpid $pid, 0;
   # say "Cannot print to invalid handle..";
}
say "ok";
Run Code Online (Sandbox Code Playgroud)

输出

ma. 25. mars 16:00:01 +0100 2019
ok
Run Code Online (Sandbox Code Playgroud)

请注意,该行开头的箭头->丢失了,因此在这种情况下无法读取任何内容$chld_out。但是,如果我注释掉该行:

local *STDOUT;
Run Code Online (Sandbox Code Playgroud)

输出是:

-> ma. 25. mars 16:01:10 +0100 2019
ok
Run Code Online (Sandbox Code Playgroud)