0
votes

I'm trying to merge a Perl script's STDOUT and STDERR streams, and fork it off to a log file.

open( MERGED, "|-", "tee /tmp/my.log" );
*STDOUT = *MERGED;
*STDERR = *MERGED;

print  "1: print\n";
warn   "2: warn\n";
system "echo 3: system call";

In this example, the "print" and "warn" messages appear in both the terminal and the log, while the system call appears only in the terminal.

How can I split my output streams off to a log file?

Note: only needs to work in Unix, and I don't want rely on packages that aren't part of the standard Perl distribution (e.g. File::Tee, IO::Tee..).

2
print qx(echo 3: backtiks) - mpapec
Is capturing/printing the output of my subprocesses the only way to achieve this? If the system call takes a while (and produces a lot of output) this isn't desirable; it'd be nice to have it flush and print in real-time, rather than collecting it all and printing it all at once. - ajwood
Perhaps open(my $sub, "-|", "echo 3" ); print while <$sub> - mpapec

2 Answers

3
votes

The child program knows nothing of Perl's variables. If you want to redirect a child's stdout, you'll need to redirect file descriptor 1.

open(my $MERGED, "|-", "tee", "/tmp/my.log") or die $!;
open(STDOUT, '>&', $MERGED) or die $!;
open(STDERR, '>&', $MERGED) or die $!;
1
votes

FYI: Here is what I came up with based on ikegami's suggestion:

$| = 1;

# Hold on to the original handles
open(my $STDOUT_ORIG, '>&', \*STDOUT) or die $!;
open(my $STDERR_ORIG, '>&', \*STDERR) or die $!;

# Fork to log
open( my $ONE, "|-", "tee /tmp/my0.log" ) || die $!;
open( STDOUT, '>&', $ONE ) || die $!;
open( STDERR, '>&', $ONE ) || die $!;

# Make some noise
print  "1: print\n";
warn   "2: warn\n";
system "echo 3: system call";

# Unhook from the first `tee`
open( STDOUT, '>&', $STDOUT_ORIG ) || die $!;
open( STDERR, '>&', $STDERR_ORIG ) || die $!;
close($ONE);

# Fork to next log
open( my $TWO, "|-", "tee /tmp/my1.log" ) || die $!;
open( STDOUT, '>&', $TWO ) || die $!;
open( STDERR, '>&', $TWO ) || die $!;

print  "A: print\n";
warn   "B: warn\n";
system "echo C: system call";

close(STDERR);
close(STDOUT);