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

I have a fairly lengthly commandline script that I would like to save debug information from. Is there an easy way to save everything that gets printed to STDOUT to a file as well? I guess what I want to do is overload print() to write to a file as well as STDOUT...Clunky by hand, I'm wondering if there's an elegant solution (Randall?)... Thanks Monks, Liam@CosmicDebris.com
  • Comment on Saving Standard Output (overloading print())

Replies are listed 'Best First'.
Re: Saving Standard Output (overloading print())
by dragonchild (Archbishop) on Aug 10, 2001 at 00:02 UTC
    Yes, there is a way to do it. It's called tie. If you tie a variable to a class and define the subroutines TIEHANDLE and PRINT, you can print to that variable as if it was a regular filehandle. Then, within the PRINT function, you can do whatever you want. You would do something like:
    package MyPrint; use FileHandle; sub TIEHANDLE { # I'll leave this as an exercise for you. } sub PRINT { my $self = shift; my $handle = $self->{FILEHANDLE}; print $handle @_; print @_; }
    Pretty simple, huh? :) If you have further questions after trying it out, post them and I'll give you some further code that I use that works.

    ------
    /me wants to be the brightest bulb in the chandelier!

    Vote paco for President!

      Ah! This is what I was looking for! Wow, I never had so many fast, knowledgable responses in a forum before! I only posted a couple of hours ago...This site rocks! Anyway, thanks to all the monks that responded, I'm gunna go learn how to tie() my handles now...;) -Liam@CosmicDebris.com
Re: Saving Standard Output (overloading print())
by shotgunefx (Parson) on Aug 10, 2001 at 00:06 UTC
    Well, ignoring shell solutions, you could use the Filter module.

    Something like this at the beginning of your script.
    use Filter::Handle qw/subs/; open (*FH, ">OUTPUT_PATH/filename.txt") or die "Could open filename.tx +t:$!"; my $output_file = *FH; Filter \*STDOUT, sub { print $output_file $_; };


    This code is untested. I hacked it from an internal program I wrote a while back that does what your asking. Let me know if you have problems.
    Update: Left out comma, duh. Fixed it.

    -Lee

    "To be civilized is to deny one's nature."
Re: Saving Standard Output (overloading print())
by suaveant (Parson) on Aug 10, 2001 at 00:00 UTC
    The easy commandline solution in unix is
    command > file
    or to see it and write to a file...
    command | tee file
    that the really simple way... (in unix, I think the > works in windows too, not sure)

    of course, this is a shell solution, not a perl one...

    Update OOPS! thanks trantor for pointing out I had > instead of | for tee command

                    - Ant
                    - Some of my best work - Fish Dinner

      Er, dude ... typo... Your second example is equivalent to "command file > tee" and will produce a file called "tee".

      Redirection and piping works in windows (kind of - it uses temporary files rather than real pipes), although I've no experience is using that from perl. Plus, you won't have "tee" unless you have the cygwin kit installed.

      Corrected, his answer is "command | tee file", which will write output to file and also standard output.

        Actually you also get "tee" with the djgpp-set of gnu programs. SO you won't actually need cygwin as they are ported to windows.
Re: Saving Standard Output (overloading print())
by faure (Sexton) on Aug 10, 2001 at 00:22 UTC
    I think I remember something like this in the Camel...
    #!/usr/bin/perl use strict; my $logfile = $ARGV[0]; open STDOUT, "|-" or do { open LOG, ">> $logfile" or die "Unable to open $logfile: $!\n"; while (<STDIN>) { print, print LOG } close LOG, close STDOUT; exit; }; print "This is only a test.\n";
    I'm sure it is much more elegant in the book... check the IPC chapter. :)

    -faure

Re: Saving Standard Output (overloading print())
by Excalibor (Pilgrim) on Aug 10, 2001 at 01:26 UTC

    Well, if it's just for debugging purposes, maybe the direct solution is just to duplicate the print sentence:

    use strict; use warnings; my $debug_flag = 0; # not debugging by default $debug_flag = $ARGV[0] if @ARGV == 1; $debug_flag = ( $debug_flag eq lc ( '-debug' ) )? 1 : 0; if ( $debug_flag ) { open DEBUG_FILE, ">debug.out" or die "Cannot open debug file for writing: $!"; } my $output; # do stuff to get the output $output = 'This is a test' . "\n"; print $output; print DEBUG_FILE $output if $debug_flag; # etc...

    Not very elegant, true, but print is still the most widely used debugging method, so let's use it...

    Another possible, easy way, (if you don't mind losing your stdout output in debug mode) would be to define something like:

    # warning, not tested my $out_fh; if ( $debug_flag ) { open FILE, ">debug.out" or die $!; $out_fh = *FILE; } else { $out_fh = *STDOUT; } # and then always do: print $out_fh $output; # it will go to the right place.

    The othe solutions are more elegant, but then debugging is always dirty, so...

    Good luck,

    our $Perl6 is Fantastic;

Re: Saving Standard Output (overloading print())
by Aighearach (Initiate) on Aug 10, 2001 at 03:35 UTC
    Here is what I use to optionally redirect output:
    if ( $opts{log} ) { open STDOUT, ">>".$opts{log} or warn "error opening log: $!"; open STDERR, ">>".$opts{log} or warn "error opening log: $!"; } else { open STDOUT, ">>/dev/null" or warn "error opening /dev/null for wr +iting: $!"; open STDERR, ">>/dev/null" or warn "error opening /dev/null for wr +iting: $!"; }
    Make sure to reopen STDOUT before STDERR, or else you won't know if STDOUT is successful.
    --
    Snazzy tagline here

      Just to note - there's a function in perl called log, so you might want to quote $opts{log} (so it becomes $opts{'log'}).

      update: I'm not saying that it would break anything, just that it might be confusing. When I looked at it, I immediately thought of the log function, not the log key. No biggie, but it might be worth quoting.

        Nyet. If you want log() then you'll need $opts{+log}, just like normal. The default idea is for a hash key to be a string, so it's the log() not the 'log' that needs clarification. Note that $opts{log(5)} isn't ambiguous, only the implicit illegal log of 0 that you get without giving it an argument.

        (I'd love to see the code that uses the log of zero for a hash key...)
        --
        Snazzy tagline here