Need Help: Capture Print Outputs from a Function
mnj200g
created: 2006-05-02 13:16:08
Hi. I am a beginner and need some PERL help. I am testing a function that I cannot change. I need to capture the print statements from this funtion. For example, the following function: sub_I_can't_change($$$) { code code print "line 1\n"; print "line 2\n"; return code; } How do I capture the outputs that it prints to the screen. I would prefer to capture the data into a variable if possible. Thanks. MNJ
Re: Need Help: Capture Print Outputs from a Function
created: 2006-05-02 13:34:02

Here's one way

#! perl -sw
use strict;

sub immutable {
    print "immutable received args: [ @_ ]\n";
    print "Some more stuff\n";
}

## Save a copy of STDOUT
open SAVED, '>&=STDOUT';
close STDOUT;

## Open STDOUT to a variable (ramfile)(Requires 5.8.x)
open STDOUT, '>', \ my( $var ) or die $!;

## Call the sub
immutable( qw[ some args here ] );

## Close the ramfile
close STDOUT;

## Redirect STDOUT back to it's original place
open STDOUT, '>&=SAVED' or die $!;

## Discard the backup
close SAVED;

## Use the captured output
print "The variable \$var now contains:\n'$var'\n";
__END__
C:\test>junk2
The variable $var now contains:
'immutable received args: [ some args here ]
Some more stuff
'



Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
Re^2: Need Help: Capture Print Outputs from a Function
created: 2006-05-02 16:31:40

That's a lot of work to avoid using select.

Re^3: Need Help: Capture Print Outputs from a Function
created: 2006-05-02 16:55:24

True, but it is guarenteed to work even if someone uses print STDOUT stuff;, when select won't.

The case of that where I got bitten is when you pass \*STDOUT to some module and internally it uses

printf { $self->{fh} } "%s\n", 'stuff';

Which is not a completely uncommon scenario.


Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
Re^4: Need Help: Capture Print Outputs from a Function
created: 2006-05-02 17:29:58
Good point! Maybe I'll start using argless [doc://select|select] instead of *STDOUT.
Re^3: Need Help: Capture Print Outputs from a Function
created: 2006-05-02 17:32:05

I prefer

{
   open local *STDOUT, '>', \$buf;
   ...
}
over
open TEMP, '>', \$buf;
my $old_select = select(TEMP);
...
select($old_select);

since the former restores STDOUT even in the case of exceptions. The former, however, doesn't work if someone has previously called [doc://select|select]. I guess the comprehensive solution would be:

{
   my $old_select = select();
   my $handle = on_release { select($old_select); };
   open local *STDOUT, '>', \$buf;
   select(STDOUT);
   ...
}

It even handles exceptions thrown by signal handlers.

Updated.

Re^4: Need Help: Capture Print Outputs from a Function
created: 2006-05-03 04:30:00
my $handle = on_release { select($old_select); };

What is on_release?


Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
Re^5: Need Help: Capture Print Outputs from a Function
created: 2006-05-03 12:34:46
Re: Need Help: Capture Print Outputs from a Function
created: 2006-05-02 13:35:52
I found this by doing a supersearch.
#!/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";
}
Output:
---------- Capture Output ----------
> "C:\Perl\bin\perl.exe" _new.pl
buffer: mysub output


> Terminated with exit code 0.
Does that help?

Update: The above requires 5.8 (thanks to [ikegami] for pointing that out).

Re^2: Need Help: Capture Print Outputs from a Function
created: 2006-05-02 14:05:19
It does not seem to be working. I get the following message: Use of uninitialized value in concatenation (.) or string at scop/x.pl line 126. buffer: Line 126 is where print "buffer: $buf\n"; is. Also, what is the structure under my ($buf); in your code? Is that how PERL creates a macro or what C calls prototypes? I've also tried the first method mentioned above and that also fail to initialize the variable $var. In that first function, what is the significant of "qw"? Thanks.
Re^3: Need Help: Capture Print Outputs from a Function
created: 2006-05-02 14:19:57
I get the following message: Use of uninitialized value in concatenation (.)

You're using a version of Perl older than 5.8.0, when open(STDOUT, '>', \$buf) was introduced. Before 5.8, \$buf would be stringified to something like SCALAR(0x1ab2760). The output ends up in a file by that name instead of going to $buf, so $buf is still undefined when you go to inspect it.

Before 5.8, you had to use [mod://IO::String] or [mod://IO::Scalar].

Also, what is the structure under my ($buf); in your code?

It creates a new scope. This causes local *STDOUT; to be undone (thus restoring the original STDOUT) before the last print.

what is the significant of "qw"

qw[ some args here ]
is the same thing as
('some', 'args', 'here')
See perlop.

Re^3: Need Help: Capture Print Outputs from a Function
created: 2006-05-02 14:22:17
I updated my node to point out that perl 5.8 is required for that to work (apologies for the delay).

Which version of perl are you using?

Re^4: Need Help: Capture Print Outputs from a Function
created: 2006-05-02 14:58:42
wfsp.

Thank you for your patience. Being a beginner, I appreciate yours and everyone else's help and patience.

Anyway, the code is working as a stand alone so I must be introducing something else that it does not like.

My PERL version is listed as 5.008005 in SunOS 5.8.

On the subject of a new scope. Does that mean I should do the following:

sub1()
{
  my $variables;
  my $buff;
  code
  code
  code

  {
    local *STDOUT;
    open( STDOUT, '>', \$buf ) or die "Write to buffer   ailed\n";
    mysub();
  }

  print "buffer: $buf\n";

  more code

}


sub mysub
{
  print "mysub output\n";
}

Again, thanks. MNJ
Re^2: Need Help: Capture Print Outputs from a Function
created: 2006-05-02 21:01:21
Thank you again to everyone. It is working. I did ran into the problem mentioned by BrowserUK where another function using select(STDOUT) and then setting "$| = 1;" (no buffering). That gave me the problem of $buf coming back empty.

I fixed that by setting buffer on ($| = 0) just before the local "scope".

Again, thank you.

MNJ

Reaped: DUP Re: Need Help: Capture Print Outputs from a Function
created: 2006-05-02 13:37:47
This node was taken out by the NodeReaper on 2006-05-02 13-50-49
Reason: [wfsp]: reap, dupe of this

You may view the original node and the consideration vote tally.

perlmonks.org content © perlmonks.org and BrowserUk, chromatic, ikegami, mnj200g, NodeReaper, wfsp

prlmnks.org © 2006 edmund von der burg (eccles & toad)

v 0.03