http://www.perlmonks.org?node_id=27233

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

When trying to help an anonymous monk I was trying to remember how to tie a scalar to STDOUT to monitor its output. So if anyone has seen this bit 'o code could please refresh my memory?
--eric

Replies are listed 'Best First'.
Re: monitor STDOUT in scalar
by btrott (Parson) on Aug 10, 2000 at 06:32 UTC
    You should be able to inherit from Tie::Handle (part of the standard distribution) and override any methods you wish to define yourself:
    package MySTDOUT; use Tie::Handle; @ISA = qw/Tie::Handle/; sub TIEHANDLE { my $class = shift; bless {}, $class; } package main; tie *STDOUT, 'MySTDOUT';
    For example, maybe you want to trap every call to STDOUT and prefix each argument with a string of your own:
    package MySTDOUT; use Tie::Handle; @ISA = qw/Tie::Handle/; sub TIEHANDLE { my $class = shift; bless {}, $class; } sub PRINT { my $self = shift; my $caller = caller; print STDERR map "$caller: $_", @_; } package main; tie *STDOUT, 'MySTDOUT'; print "Foo";
    Prints
    main: Foo
    Take a look at the Apache module and Apache::Filter for some examples of tying STDOUT.
      That is very cool! It also answers a question I asked some time ago, about Line Numbers. You could use this to put line numbers in your debug log:
      package MyDEBUGLOG; use Tie::Handle; @ISA = qw/Tie::Handle/; sub TIEHANDLE { my $class = shift; bless {}, $class; } { my $line = 0; sub PRINT { my $self = shift; ++$line; print DEBUGLOG map "$line: $_", @_; } } package main; tie *DEBUGLOG, 'MyDEBUGLOG'; print DEBUGLOG "Foo";
      Or so I would suspect. Very cool.
        Well, not quite, because DEBUGLOG isn't opened for writing, for one thing, and for another, it's tied in a different package than when you're actually writing to it (main vs. MyDEBUGLOG).

        And even if you do work out those issues, I think you'll have a problem with "Deep Recursion". You're writing to a tied filehandle, which invokes the PRINT method on your tied object, which... writes to the same tied filehandle. Hence recursion.

        Check out my new Snippet, Filehandle Filter. With that you could do this:

        use Filter::Handle; local *DEBUG open DEBUG, ">debuglog" or die $!; my $f = Filter::Handle->new(\*DEBUG); print DEBUG "Foo"; print DEBUG "Bar";
Re: monitor STDOUT in scalar
by tilly (Archbishop) on Aug 10, 2000 at 07:15 UTC
    This is not quite the question I thought of in the chatterbox. Use the IO::Scalar module from CPAN.
    use IO::Scalar; my $capture; tie(*CAPTURE, 'IO::Scalar', \$capture); select (CAPTURE);
    And now the output goes into $capture. For details I suggest reading up on tie.

    BUT this approach has a big gotcha. Any and all output that comes from Perl will be captured. Any and all output that is done in C is unlikely to be. So for instance any print you do goes to $capture. But if you make a system call, not a chance.

    The safer approach is to run a second process like this:

    local *CAPTURE; my $cmd = join " ", "perl", "script_name", @args); open (CAPTURE, "$cmd |") or die "Cannot run command $cmd: $!";
    And now the STDOUT of the second process can be read and processed by the first. (On Unix you could actually do a fork and have one process read from the other - the Cookbook has an example.)

    EDIT
    If people are actually reading this I would like to point out a minor detail. Note how I put the command to run into a variable? I make it a point to always do that so that I don't have to synchronize code to get the debugging message to correctly report what failed. Even if it means having a line like:

    my $file = "foo.txt";
    A small but significant detail I like to pay attention to. :-)