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

I have a perl module, let's say its called Library.pm, and it contains a lot of subroutines. Some of these subroutines do not return anything, but just print something out. For example I have a subroutine printInformation() that just prints info to STDOUT. eg,
package Library.pm printInformation() { print(STDOUT, "Here is some info, damn its not returning a scalar"); }
The problem with the above subroutine example is that I want to hold on to the string that gets printed to STDOUT (or STDERR), but I cannot modify Library.pm. So instead I need to write LibraryWrapper.pm that massages the subroutines of Library.pm to have returnable scalars, arrays, or hashes, etc. . Below demonstrates what I am trying to do:
package Library.pm use Library.pm printInformationWrapper() { my @captureSTDOUT; my @captureSTDERR; # Set state so that: # @captureSTDOUT is populated with all STDOUT. # @captureSTDERR is populated with all STDERR. printInformation(); # Set state back to previous if (@captureSTDOUT > 0) { # Deal with it. } return @captureSTDOUT; }
Please also bear in mind that I would like a solution that is portable for any platform, and is completely self-contained, ie. all done in one process, (no forks). Lastly, just in case, I know about the backticks operator but it only seems to work on system commands, or more generally commands from the parent environment. It does not work when one perl subroutine calls another in the same process. I'd appreciate the help a lot, thanks.

Replies are listed 'Best First'.
Re: n a perl sub, how do I capture STDOUT, STDERR that comes from another perl sub?
by akho (Hermit) on Aug 17, 2007 at 11:59 UTC
    You can localize STDOUT and STDERR this way:
    sub printInformationWrapper { open (local *STDOUT,'>',\(my $var)); thing that prints to STDOUT return $var; }
    This method sometimes has problems with printing Unicode. (unless you use open ':utf8'; use encoding 'utf8';, which is the right way to go for unicode anyway).

    This is supposed to work without the pragmata (http://community.livejournal.com/ru_perl/183306.html):

    { open (local *STDOUT,'>:utf8',\(my $b="\x{FEFF}")); printing_stuff return $b; }
    though I've not tested it.

    IO::String is probably a less magical way to tie a filehandle to a string. Then you localize STDOUT like above, assigning your filehandle.

      open (local *STDOUT,'>:utf8',\(my $b="\x{FEFF}"));

      I was bitten in the neck by this, at least with :raw. I don't know if with :utf8 it's the same. Of course a separate binmode is a viable workaround. Yes, I'd like to do that too in the open to start with.

Re: n a perl sub, how do I capture STDOUT, STDERR that comes from another perl sub?
by moritz (Cardinal) on Aug 17, 2007 at 12:02 UTC
    In the open doc there is an example:

    close STDOUT; open STDOUT, '>', \$variable or die "Can't open STDOUT: $!";

    You could combine that with local *STDOUT to limit that capturing to a scope:

    #!/usr/bin/perl use warnings; use strict; sub writes_to_stdout { print "some information\n"; } sub captured { local *STDOUT; my $result; open STDOUT, '>', \$result; writes_to_stdout(); return $result; } my $val = captured(); print "No output yet\n"; print "But now: $val"; ## and this is what it writes: $ perl foo.pl No output yet But now: some information

    Update: added complete example

      Thank you very much to both of you, this exactly what I needed. Works like a charm.