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

Hi --

When redirecting STDOUT to a scalar,

tie *STDOUT, 'IO::Scalar', \$stdout;
output doesn't go to the screen (obviously). Is there a way to 'tee' it, so output goes both to the screen and captured in the scalar? How does / Does IO::Tee play with IO::Scalar?

Thanks!

water

Replies are listed 'Best First'.
Re: Tee STDOUT
by water (Deacon) on Sep 24, 2004 at 02:21 UTC
    Here's the context: I want to capture the results of Test::Harness runtests into a string, while it also goes to the screen. Here's what I have:
    my $stdout = ''; eval { *STDOUT = new IO::Tee( \*STDERR, IO::Scalar->new(\$stdout)); $ok = runtests(@tests); };
    It errors out with
    write() on unopened filehandle STDOUT at /usr/lib/perl5/vendor_perl/5. +8.2/Test/Harness.pm line 665.
    How can I capture runtest output to a string as well as the screen?

    Thanks!

    water

      I don't have IO::Scalar, but the following works. Downside: requires perl 5.8.0 or higher.
      use 5.8.0; # for open \$buf. use strict; use warnings; use IO::Tee (); my $stdout = ''; { my $fh_scalar; open($fh_scalar, '>', \$stdout) or die("Can't open \$stdout: $!\n"); local *STDOUT = new IO::Tee( \*STDERR, $fh_scalar ); print("Test\n"); } print("\n"); print("\$stdout:\n"); print($stdout);

      I added local to easily restore the original STDOUT, but it's not needed.

        Thanks, but I tried your code and still get
        write() on unopened filehandle STDOUT at /usr/lib/perl5/vendor_perl/5. +8.2/Test/Harness.pm line 665.
        Maybe this has something to do with the magic internals and filehandle remapping already going on in Test::Harness?
Re: Tee STDOUT
by eyepopslikeamosquito (Archbishop) on Sep 24, 2004 at 09:37 UTC

    Testing stuff written to stdout has been discussed a few times on the perl-qa mailing list. As far as possible, I use Ovid's trick of something like:

    sub Foo::_print { print shift }

    Then in my test, something like this:

    my $_print; *Foo::_print = sub { $_print = shift }; is($_print, $expected, "Foo::_print output the correct data");

    Apart from sparing you worrying about Test::Harness choking, it makes the module a little more flexible in that you can (a little hackily) override the _print function if you need to alter the module behaviour.

    If you can't use a _print() function for some reason, there are many other approaches as discussed here and here and here and here. For an example of using tie, see the file TieOut.pm in the ExtUtils-MakeMaker CPAN distribution.

Re: Tee STDOUT
by ikegami (Patriarch) on Sep 24, 2004 at 02:07 UTC
    I'd start by taking a look at IO::Tee. Update: oops, you alluded to it! I'm blind. It should interact fine, as long as you call functions that IO::Scalar supports. I don't know if binmode is supported, for example. Why don't you write a quick test?