I have a very large program that writes a lot of things to STDOUT
, I'd like to be able to feed all of that output to both STDOUT
and to an arbitrary FILE
. I've decided to use IO::Tee
to create the shared writeable handle:
use IO::Tee;
pipe(my $reader, my $writer);
my $new_stdout = IO::Tee->new(\*STDOUT, $writer);
*{STDOUT} = $new_stdout;
print "foo";
print(do { local $\; <$reader> });
However, this causes a deep-recursion and crashes the program. So, instead I can not reference *STDOUT
, and it creates it:
use IO::Tee;
pipe(my $reader, my $writer);
my $new_stdout = IO::Tee->new(*STDOUT, $writer);
*{STDOUT} = $new_stdout;
print "foo";
print(do { local $\; <$reader> });
This creates the warning: Undefined value assigned to typeglob at ... line 42
, and when I use Data::Printer
to describe $new_stdout
, it is undef
. How can I do this?
Almost all code that accepts a file handle accepts it in many forms:
\*STDOUT
)*STDOUT
)*STDOUT{IO}
)Here is no exception. Instead of a passing a reference to a glob you later modify, you can solve this problem by passing the IO object originally assigned to STDOUT. This makes the later modification of *STDOUT
moot.
Replace
IO::Tee->new( \*STDOUT, $writer )
with
IO::Tee->new( *STDOUT{IO}, $writer )
That leaves you with a number of other problems, though.
$new_stdout->autoflush;
).[just pipe its output to
tee
] would be ideal... But we're hoping to be able to package this as a library, such that we can e.g use Stdout::Replace
package Stdout::Replace;
use IPC::Open3 qw( open3 );
my $pid = open3( local *CHILD_STDIN, ">&STDOUT", ">&STDERR", "tee", "file.out" );
open( STDOUT, ">&", *CHILD_STDIN ); # dup2
close( CHILD_STDIN );
STDOUT->autoflush();
END {
close( STDOUT );
waitpid( $pid, 0 );
}
1;
Demo:
$ cat file.out
cat: file.out: No such file or directory
$ perl -I lib -Mv5.14 -e'use Stdout::Replace; say "foo";'
foo
$ cat file.out
foo
You can dup2 CHILD_STDIN onto STDERR too, if you so desire.
You can save and restore the original STDOUT as follows:
open( my $orig_stdout, ">&", *STDOUT );
...
open( STDOUT, ">&", $orig_stdout );