Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I would like to redirect STDOUT to a variable. This has been covered before, specifically here. Also several other times (search 'redirecting stdout'). However, I would like it to work for system calls. What I have is:
local (*STDOUT, *READOUT, *WRITE); pipe READOUT, WRITE or die $!; open STDOUT, '>&WRITE' or die $!; close WRITE; @result = &$code(); close STDOUT; my $stdout = <READOUT>;


The problem is that if $code contains a system call its output does not get redirected.

Replies are listed 'Best First'.
Re: redirecting STDOUT (of a system call) to a var
by kschwab (Vicar) on Jun 28, 2001 at 02:41 UTC
    Here's a low tech solution:
    #!/usr/bin/perl -w use strict; pipe READER,WRITER or die; my @output; if (my $pid = fork) { # parent, close the WRITER and read from READER close WRITER; while(<READER>) { chomp; push(@output,$_); print "adding $_ to \@output\n"; } # parent now has the system() output in @output, # as well as the line from the child's print() } else { # child, dup WRITER to STDOUT and close WRITER, then # exec() your system call open(STDOUT,">&WRITER"); close WRITER; print STDOUT "This is the child running ls\n"; exec("/bin/ls"); }
    Update: I guess I should point out that the child could be mixing perl STDOUT writes with the system calls' STDOUT writes. I think the original poster wanted to run a code ref, including system calls and have all the writes to STDOUT output to a perl variable.
Re: redirecting STDOUT (of a system call) to a var
by particle (Vicar) on Jun 28, 2001 at 06:17 UTC
    i've been alltogether unsatisfied with this situation myself, as seen in a past post of mine~
    once again: program output and return code. though in my environment, i was able to change code to use IPC::Open3, and capture STDOUT and STDERR.

    what i cannot do is capture STDOUT, STDERR, and the exit code. hopefully at some point in the future, this will be remedied.

    ACK!!!

    ~Particle

      Doesn't calling waitpid() (as documented) after using open2() or open3() also give you the exit status?

              - tye (but my friends call me "Tye")

        BRILLIANT!

        now i call waitpid(-1,0), and the value of $? is the return value from the open! this will make my programming life MUCH easier!

        thank you tye.

        in case this helps anyone else, the code that's changed is:

        eval { $pid = open3("<&STDIN", \*OUTPUT, \*OUTERR, 'perl return.pl') ; $val = waitpid(-1,0); # <--- added this line }; ... print "---pid$n"; print $pid . $n; # <--- prints pid print "---val$n"; print $val . $n; # <--- prints pid print "---\$?$n"; print $? >>8, $n; # <--- prints exit val
        i am amazed. and VERY happy.

        ~Particle

Re: redirecting STDOUT (of a system call) to a var
by runrig (Abbot) on Jun 28, 2001 at 02:47 UTC
    Am I missing something? Wouldn't backticks or qx instead of system do what you want? Or do you have no control over the code in that coderef?
      To clarify, yes I have no control over the coderef. It can contain an arbitrary mix of system calls and Perl print calls.
Re: redirecting STDOUT (of a system call) to a var
by mr.nick (Chaplain) on Jun 28, 2001 at 03:02 UTC
    Update: AM is correct: I didn't read the post correctly. -- at will, I deserve it.

    mr.nick ...

      Why don't you try reading the post Mr. Nick. You will see a link to the same node you are pointing to, and a comment about using search.
self answer (was: Re: redirecting STDOUT (of a system call) to a var)
by Anonymous Monk on Jun 29, 2001 at 05:38 UTC
    open SAVE_OUT, '>&STDOUT'; pipe READOUT, WRITE; open STDOUT, '>&WRITE'; close WRITE; @result = &$code(); close STDOUT; my @stdout = <READOUT>; open STDOUT, '>&SAVE_OUT';
    apparently the local was messing it up.

      This will only work if your output is less than one "pipe bufferful" (512 bytes or some small multiple thereof). Attempts to write more than that amount of output will cause your script to hang.

              - tye (but my friends call me "Tye")