tags:

views:

496

answers:

7

How can I print to a variable with Perl?

I've been working on a program for a while which logs its iterative progress in a highly verbose fashion...

print $loghandle $some_message;

However, I'd like to also selectively print some of the messages to a different file. Naturally, I could sprinkle the code with...

print $loghandle $some_message
print $otherloghandle $some_message

Or rewrite the whole business into a function. Blah.

What I want to do is do some magic when I open the $loghandle so that when I'm print'ing, I'm actually just doing a sprintfish operation against a variable(call it $current_iteration), so that when I get down to a decision point I can do something like this...

print $real_log_file $current_iteration;
print $other_real_log_file $current_iteration if($condition);

I'm fairly sure I've seen something like this somewhere, but I have no idea where it is or where to look.

edit: File::Tee solves this problem to some extent on *nix, but I run on Windows.

+2  A: 

You can use File::Tee to split a filehandle into multiple output streams.

use File::Tee;
open my $fh, '>', 'logfile.txt' or die $!;
tee( $fh, '>', 'otherlogfile.txt' ) if $condition;

print $fh $current_iteration;   # will also go to otherlogfile.txt 
                                # if $condition was true
friedo
I run on Windows(File::Tee doesn't). adding that to the question.
Paul Nathan
+1  A: 

Sound like you want to tie your filehandle.

my $x;

# printing to $fh will update the variable $x
# when you close $fh, it will print $x to a filehandle depending
# on code in the function  Print_to_variable::CLOSE 

tie $fh, "Print_to_variable", \$x, $output_fh1, $output_fh2;
print $fh "stuff";
print $fh "more stuff";
close $fh;

sub Print_to_variable::TIEHANDLE {
    my ($class, $ref, $fh1, $fh2) = @_;
    my $self = {};
    $self->{ref} = $ref;
    $self->{output_fh1} = $fh1;
    $self->{output_fh2} = $fh2;
    bless $self, "Print_to_variable";
    $self;
}
sub Print_to_variable::PRINT {
    my ($self,@list);
    ${$self->{ref}} .= join "", @list;
}
sub Print_to_variable::CLOSE {
    my $self = shift;
    my $text = ${$self->{ref}};
    if ( &myCondition1($text) ) {    # ... how you decide where to print 
        print {$self->{output_fh1}} $text;
    } else {
        print {$self->{output_fh1}} $text;
    }
}
mobrule
I think so, but I have the dumb today and don't see how tie actually works here.
Paul Nathan
+2  A: 

Perlfaq5 recommends Tie::FileHandle::Multiplex for printing to multiple files.

The source is very simple and it should be easy to modify with a per-handle filter.

daotoad
+4  A: 

Do you mean something like IO::Scalar? Lets you write to a variable with filehandle semantics.

Andrew Barnett
You don't need IO::Scalar explicitly. open() by itself works just fine.
brian d foy
A: 
Paul Nathan
I am amused! And I wasn't sure about the aliasing/referencing args to functions. Thank you!
Paul Nathan
+8  A: 

You can treat a scalar variable as a filehandle by opening it:

open my $fh, '>', \$variable or die "Can't open variable: $!";
print $fh "Treat this filehandle like any other\n";

You can even map stdout or stderr to a scalar:

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

If you want to split your output or set up a config file to do "interesting" things with your logging, you are better off with Log4Perl as others have suggested.

Ether
+4  A: 

If you want to do selective logging where you can control which messages are logged and where they are logged, use Log::Log4perl. That will save you a bunch of time over messing with ties and other black magic.

brian d foy