Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

how to control/redirect print()

by smile4me (Beadle)
on Apr 03, 2011 at 00:52 UTC ( [id://897133]=perlquestion: print w/replies, xml ) Need Help??

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

Is there a way to control when print() actually goes to STDOUT? I have a LOTS of legacy code that has embedded print statements. I'd like to rearrange when things print without having to rewrite all the subroutines. For example,

$str = q[new text]; $str .= sample( qw(1 2 3) ); $str .= q[more new text]; print_now( "$str \n" ); sub sample { print q[sample(): ], @_, "\n"; } sub print_now { say @_; ## or print $FH @_; }

Is there some trick to prevent the text in sample() from print'ing first? Can I redirect STDOUT to a variable in the code?

Replies are listed 'Best First'.
Re: how to control/redirect print()
by GrandFather (Saint) on Apr 03, 2011 at 01:23 UTC

    One technique is to use select to redirect STDOUT to a different file handle. It's often convenient to use a file opened on a string:

    #!/usr/bin/perl use warnings; use strict; my $log; open my $logOut, '>', \$log; my $oldStdOut = select $logOut; print "this goes to the log string\n"; select $oldStdOut; close $logOut; print "From log: $log";

    Prints:

    From log: this goes to the log string
    True laziness is hard work
Re: how to control/redirect print()
by wind (Priest) on Apr 03, 2011 at 02:38 UTC

    Take a look at perldoc open and search for each instance of 'open STDOUT'. It has example code on how to duplicate and redirect both STDOUT and STDERR.

    Grandfather's code will probably work for you, but it's good to know where in the documentation you can look for the random times you need to do this and have forgotten how.

Re: how to control/redirect print()
by Marshall (Canon) on Apr 03, 2011 at 06:09 UTC
    Yes, you can redirect STDOUT to a variable. But you need some kind of statement to select between "normal STDOUT" and the "variable STDOUT". Another possibility is open a filehandle to a variable and just add that filehandle after the print's that you want to go there. And then just print that variable when you want that output to show up? Note: all I/O routines return status, including print - that's what causes this extra '1' to show up in the printout and it is the default return value from sample(), assuming the print succeeds which it almost always does.

    I guess there are some other possibilities here, depending upon whether you are writing to a file or to something that Perl can identify as the console. STDERR is non-buffered while STDOUT is buffered when writing to pipes,files. STDERR will come out right NOW, vs STDOUT which will print when it's buffer is full, maybe 4-8K bytes or so.

    #!/usr/bin/perl -w use strict; use 5.10.0; ## needed for "say" print "First, Order as written:\n"; my $str = q[new text]; $str .= sample( qw(1 2 3) ); #adds '1' to $str $str .= q[more new text]; print_now( "$str \n" ); sub sample { print q[sample(): ], @_, "\n"; } sub print_now { say @_; ## or # print $FH @_; # this an error what is $FH? } print "Now, reverse order:\n"; #################### my $delayedout; open (DELAY, '>', \$delayedout) or die "$!"; $str = q[new text]; $str .= sampleb( qw(1 2 3) ); $str .= q[more new text]; print_nowb( "$str \n" ); print $delayedout; sub sampleb { print DELAY q[sample(): ], @_, "\n"; } sub print_nowb { say @_; } __END__ PRINTS: First, Order as written: sample(): 123 new text1more new text # 1 is return value of sample # extra \n comes from say statement. Now, reverse order: new text1more new text sample(): 123
Re: how to control/redirect print()
by hesco (Deacon) on Apr 03, 2011 at 01:47 UTC
    I don't remember the details of how I did this, but this was necessary for a module I published years ago to CPAN, Test::MonitorSites. A review of that code will likely turn up some useful clues.

    UPDATE:

    OK, this seems to be the code which did the trick:

    my $Test = Test::Builder->new; my @handle_names = qw/ output failure_output todo_output /; my %old; $old{$_} = $Test->$_ for @handle_names; $Test->$_(\*STDOUT) for @handle_names; # which was restored like so . . . $Test->todo_output(*STDOUT); $Test->failure_output(*STDERR); $Test->output(*STDOUT);
    -- Hugh

    if( $lal && $lol ) { $life++; }
    if( $insurance->rationing() ) { $people->die(); }
Re: how to control/redirect print()
by smile4me (Beadle) on Apr 04, 2011 at 18:43 UTC

    So, listening to what GrandFather, Marshall, and others have said. Here's the refactored code:

    use 5.010; use strict; use warnings; my ($fh, $str); open ($fh, '>>', \$str) or die "$!"; my $oldSTDOUT = select $fh; $str .= qq[first line: \n]; say q[can we control the print from a sub? ]; sample( qw(1 2 3 ) ); $str .= sample( qw(4 5 6 ) ); say q[ well... ]; select $oldSTDOUT; close $fh; print_now( $str ); sub print_now { my $txt = shift; $txt =~ s/e/E/g; say "txt: $txt"; } sub sample { print 'in sample(): ', @_, "\n"; } __END__ ## output txt: first linE: can wE control thE print from a sub? in samplE(): 123 in samplE(): 456 1 wEll... ## the '1' from sample(456)

    Lessons learned:

    • open in append mode so we can add to the $str.
    • anything going to STDOUT is redirected to the variable
    • the legacy subroutine's print is appended to the $str when called inline, but
    • the return value of the print is appended to the str when called in concatenation (scalar mode, terminology?)

    Problem Solved!

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://897133]
Approved by GrandFather
Front-paged by moritz
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (4)
As of 2024-04-19 02:11 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found