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

Is there a way to get a copy of STDOUT without redirecting STDOUT? I wanted to capture the printouts from a sub that I cannot change. The following code is doing that (my thanks to others on this board for this code):

#!/usr/bin/perl use strict; use warnings; my ($buf); { local *STDOUT; open( STDOUT, '>', \$buf ) or die "Write to buffer failed\n"; mysub(); } print "buffer: $buf\n"; sub mysub{ print "mysub output\n"; }

The sub's printouts go to STDOUT, which is to my screen. Using the above code, STDOUT is redirected to the variable $buf so no printouts go to the screen. I want to be able to get a copy of the data that is going to STDOUT without redirecting STDOUT. In other words, I want the data to go to the variable "$buf" in the above code and to continue to go to the screen. Is there a way to do this?

I could just print the variable $buf after its done too. However, some of data are very big and the sub that I'm trying to capture takes a long time to execute. During that time, no printouts goes to the screen so there's nothing indicating that the program is still executing correctly.

Also, is there a way to capture STDERR at the same time? I mean, can I redirect a copy of STDERR and STDOUT to the same $variable? I was able to do it to two variables, but then, I don't know which error occur when. It would be nice to have the STDOUT and STDERR in the same spot in chronological order so I know which error occurred after which STDOUT data.

Thanks for any help.

MNJ

Replies are listed 'Best First'.
Re: Need Help: Capture STDOUT without Redirecting
by philcrow (Priest) on May 04, 2006 at 20:07 UTC
    I haven't used it, but the Cookbook recommends IO::Tee which allows you to create a scalar file handle. When you print to it, the output goes to as many handles as you like. This is explained recipe 7.8.

    Phil

      IO:Tee does seem interesting.

      However, I cannot modify the sub whose outputs I want. Also, I don't have the module and doubt that the admin would install it.

      MNJ

        However, I cannot modify the sub whose outputs I want.

        Not a problem.

        #!/usr/bin/perl use strict; use warnings; use IO::Tee (); my $buf; { open(my $buf_fh, '>', \$buf ) or die "Unable to create a write-to-memory handle: $!\n"); my $tee = IO::Tee->new(\*STDOUT, $buf_fh); my $old_select = select($tee); mysub(); select($old_select); } mysub(); } print "buffer: $buf\n"; sub mysub{ print "mysub output\n"; }

        The above is untested. If it doesn't work, I'm sure it's fixable.

        Update: Tested (as much as I can with Perl v5.6.1) and fixed. Keep in mind the previously discussed caveats to using select.

        I don't have the module and doubt that the admin would install it.

        Then install it in your account. This is super easy in this case since the module consists entirely of one .pm.

        IO::Tee is pure Perl code. If you can copy a file onto the same machine you're running your code (and if you can't, one wonders how you're getting your code there . . .) you can "install" your own copy. Make an IO directory somewhere, put the IO::Tee source in Tee.pm, point perl at the directory containing IO via either PERL5LIB, a command line -I switch, or the standard lib module.

      I am stuck on not using IO:Tee. In my environment, it is considered unvalidated code. All code, including my test scripts, must go through a detailed review and documentation. Any code that I use must go through the same vigorious process. If I use IO:Tee, it will have to go through the same process. I think this would be more problem than it's worth at the moment.

      Before test scripts are executed, I do start UNIX's "script" to capture the log. From my limited experience with "script", it seems that the log file does not get written to until the "script" performs an "exit". I suppose I could call "script" inside of a perl script. Would I have problems identifying which session is the original session and which is the "script" session?

      MNJ

Re: Need Help: Capture STDOUT without Redirecting
by jpeg (Chaplain) on May 04, 2006 at 21:20 UTC
    I feel dirty for even suggesting it, but perldoc perlsub tells you how to override builtin functions. I hacked up an example overriding 'print' that called CORE::print twice - once to STDOUT and once to a filehandle, so I know 'print' can be overridden. That won't work for you: I needed to call my print as &print and you said you've no access to the sub.

    I feel even dirtier suggesting you look into importing your print function into CORE::GLOBAL.

    It's definitely dangerous, but hey, the functionality is there.

    --
    jpg
      You can't override print, because of its weird syntax for accepting file handles. You can create functions called print, of course, but you can't override print. That's why you had to change print to &print.
Re: Need Help: Capture STDOUT without Redirecting
by gam3 (Curate) on May 05, 2006 at 02:27 UTC
    You can use Tie::Handle to capture the output to a scalar and then do what ever you need to it.
    package NewHandle; require Tie::Handle; @ISA = qw(Tie::Handle); sub WRITE { $self = shift; $main::text .= shift; } sub TIEHANDLE { bless { }, shift; } package main; use strict; use warnings; our $text; print "before: $text\n"; { local *STDOUT; tie *STDOUT, 'NewHandle'; mysub(); } print "after: $text\n"; sub mysub { print "mysub", " output\n"; } 1;
    -- gam3
    A picture is worth a thousand words, but takes 200K.
Re: Need Help: Capture STDOUT without Redirecting
by xdg (Monsignor) on May 04, 2006 at 20:11 UTC

    Maybe something like IO::Tee would help?

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: Need Help: Capture STDOUT without Redirecting
by TedPride (Priest) on May 04, 2006 at 21:14 UTC
    You could also just run a utility that logs your readout to a file (perhaps script?). This would let you read the display in real-time, while still having a record of your session. I know it's an unPerlish solution, but it's also probably the easiest solution to implement.
      Like script | tee logfile
Re: Need Help: Capture STDOUT without Redirecting
by TedPride (Priest) on May 05, 2006 at 05:16 UTC
    Did you miss the part where he said:

    "I could just print the variable $buf after its done too. However, some of data are very big and the sub that I'm trying to capture takes a long time to execute..."