I want to temporarily redirect stdout to an in memory variable. Prints are correctly redirected to my variable but not the output of a pipe (bc in my example). What is going on?
#!/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";
Actual ouput will be:
before bc
Expected output:
before bc
4
You have a detailed explanation in mob's answer of why you cannot redirect a child's STDOUT
to a variable, which isn't really a filehandle.
Instead, you can use a module for running external programs, that can redirect standard streams to variables. Then you can combine strings with redirected output as you wish.
An example with 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;
Here I used select to manipulate where prints go by default (without a filehandle specified).
Note that the example redirects run3
to a different variable ( $so_2
) than the one used for a previous redirect. If you'd rather append to the same variable specify this in %options
run3 ["ls", "-l", "./"], undef, \$so_1, { append_stdout => 1 };
and remove $so_2
from the printing statement.
The module uses temporary files for this redirection, as mob also indicated in the answer.
Some other options are Capture::Tiny that can redirect output from nearly any code, with a simple and clean interface, and the very powerfull, rounded, and more complex IPC::Run .
This is ... not possible.
Standard output of piped opens and system
calls are written to file descriptor 1. Normally, Perl's STDOUT
file handle is associated with file descriptor 1, but that can be manipulated.
In this example, the system
calls writes to STDOUT
filehandle, which writes to the file 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"
But in this example, the system
calls writes to the BAZ
filehandle.
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"
An in-memory filehandle is not a real filehandle. If you call fileno
on it, you will (generally, may be OS dependent) get a negative number.
open STDOUT, '>', \$scalar;
print STDERR fileno(STDOUT); # -1
Piped opens and system calls will not be able to write to this filehandle.
You will need a more complicated workaround, like writing the piped open output to a file, and then copying that file into the in-memory variable.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.