The problem presumably is the following: the way you're connecting STDOUT to the pipe is
changing the file descriptor number so that it's no longer 1. However, file
descriptor 1 is what your exec'ed child process is assuming to be stdout (i.e. the default).
printf STDERR "before: fileno(STDOUT)=%d\n", fileno(STDOUT);
open $old_stdout, ">&STDOUT" or die "open: $!";
close( STDOUT );
pipe( $smash_stdout, STDOUT );
printf STDERR "after: fileno(STDOUT)=%d\n", fileno(STDOUT);
prints
before: fileno(STDOUT)=1
after: fileno(STDOUT)=6
Note that fileno changed from 1 to 6. In other words, you now have fileno 6 connected to the pipe (something an exec'ed child would know nothing about...)
Something like this might work better
printf STDERR "before: fileno(STDOUT)=%d\n", fileno(STDOUT);
open $old_stdout, ">&STDOUT" or die "open: $!";
# _don't_ explicitly close STDOUT here
local(*RH, *WH);
pipe RH, WH;
open STDOUT, ">&WH" or die "open: $!";
printf STDERR "after: fileno(STDOUT)=%d\n", fileno(STDOUT);
because this way, STDOUT keeps being associated with file descriptor 1:
before: fileno(STDOUT)=1
after: fileno(STDOUT)=1
Update: a simple demo:
open $old_stdout, ">&STDOUT" or die "open: $!";
local(*RH, *WH);
pipe RH, WH;
open STDOUT, ">&WH" or die "open: $!";
# write something to the pipe
print "foo";
# and have some fork/exec'ed process write to the pipe (via stdout)
system('echo bar');
close WH;
my $out = <RH>;
print STDERR "got from pipe: $out"; # $out is "foobar\n"
|