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

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

I have a situation to write output to a file and also print on console or CMD. Need feedback on my non-working code. Thanks.

#!/usr/bin/env perl -w use strict; use warnings; use Fcntl; use Tie::File; #-- saving result to output.txt open STDOUT, ">", "output.txt" or die "$0: open: $!"; #-- write to output.txt and print on console print "##Start of SCRIPT.PL##\n"; my $run = "SCRIPT.pl"; system($run); #-- open the file tie my @rows, 'Tie::File', 'output.txt', autochomp => 0 or die "error: + $!\n"; #-- print last line of output.txt on console print "$rows[-1]";

Replies are listed 'Best First'.
Re: Output to STDOUT and print last line to console
by Loops (Curate) on Oct 24, 2014 at 05:04 UTC

    The problem is that your final print statement is being sent to the end of output.txt instead of the console. STDOUT is still redirected at that point. There are several ways to remedy the situation; here is one using backticks (`) to capture the output of the script you run:

    use Fcntl; use Tie::File; open my $out, ">", "output.txt" or die "$0: open: $!"; my $run = "./SCRIPT.pl"; print $out "##Start of SCRIPT.PL##\n", `$run`; close $out; tie my @rows, 'Tie::File', 'output.txt', autochomp => 0 or die "error: + $!\n"; print ":$rows[-1]";

    In the above case there is no pressing need to use Tie::File which will be less efficient. Hopefully this was just a small snippet of a larger work where its use is more warranted. :o)

Re: Output to STDOUT and print last line to console
by Discipulus (Canon) on Oct 24, 2014 at 11:14 UTC
    hello wayto perl

    I think Log4perl can do this in the right way.

    The ability to multiplex the output (this seems the correct term) of Log4Perl can help you a lot, but if you want to try to code it by hand you can explore my similar 'proof of concept' approach in Multiplexing log output: Log4perl of the poors.

    HtH
    L*
    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: Output to STDOUT and print last line to console
by RonW (Parson) on Oct 24, 2014 at 16:24 UTC

    Loops is right about the redirection still being in effect.

    Another way to print to console is:

    print STDERR "$rows[-1]";
Re: Output to STDOUT and print last line to console
by Don Coyote (Hermit) on Oct 25, 2014 at 21:59 UTC

    Hi waytoperl

    I would side with Discipulus here and surmise that your problem is in regard to simultaneously printing the output into multiple filehandles, or multiplexing.

    For me it seemed clear that using the single form of perlfunc:select would be necessary to achieve simple multiplexing. However, I managed to rework the code around the perlfunc:pipe function. Thank Strawberry (and Perlmonks) for perldoc.

    Essentially, once you have selected a filehandle, the filehandle_special-variables are operational on that filehandle

    Any regular output run in a perlfunc:system call will go to that output filehandle, but there, as in the code here, any redirections within the external script, will behave as scripted.

    The perlfunc:pipe perlfunc:fork combo sets up a situation where you may select the output filehandle to only print to the output, or select the pipe $writer indirect filehandle, to print to both (or as many as you like) at the same time.

    Best not to redirect 0,1,2 unless you understand it well. Even though I have done so here, I am very confident there are better ways. Discipulus' link to log4Perl premise is definetly worth another look.

    #!/usr/bin/perl -w use strict; use feature qw{ say }; use File::Spec::Functions qw{ catfile }; use Fcntl; use IO::Handle; # special Filehandle variables, # operate on currently selected fh $|++; open my $output, '>', catfile( qw( . output.txt ) ) or die q{ output open fail },$!; my $original_selected_default_output = select $output; $|++; select $original_selected_default_output; my $reader = IO::Handle->new(); my $writer = IO::Handle->new(); (select($_),$|++) foreach ($reader,$writer,'STDOUT'); pipe($reader,$writer) or die "not today $!"; my $pid = fork(); if( $pid == 0 ){ close $writer; open my $stdout, '>&', 'STDOUT' or die 'aaaarrrggh!',$!; my @filehandles = ( $stdout, $output ); say( 'firstly from reader' ) foreach($stdout,$output); while(my $l = <$reader>){ foreach my $fh ( @filehandles ){ say { $fh } 'from ',$l, ' reader'; } }continue{} # this is distilled genius say( 'finely from reader' ) foreach( $stdout, $output ); select $original_selected_default_output ; close $reader; exit 0; }elsif($pid){ close $reader; }else{ die "there is far better error handling to be had here $!" } # say to ./output.txt select $output; say localtime(time),q{ All I need to say is use feature }; # say to both select $writer; say localtime(time),' using say is straightforward for simple output'; # set back to good ol' STDOUT select $original_selected_default_output or die "not working $!"; # clear up the pipe close $writer; #scrap most of todays code #sleep 1; say localtime(time),q{ snoozing}; say 'quick, gotta go to bed'; exit 0;

      Hi Don. I like your idea and tried your code out a bit. You mentioned in your intro that you think there are better solutions possible; you'll probably like this one, since it doesn't require a separate process:

      use strict; use warnings; use feature 'say'; use autodie qw( :all ); open my $log, '>', 'output.txt'; tie *BOTH, 'Multiplex', *STDOUT, $log; select *BOTH; say 'Sent to display and log : ', 'tested'; printf "%s should get %s\n", qw( Both this ); select *STDOUT; say 'Sent to display only'; select $log; say 'Sent to log only'; package Multiplex; use parent 'Tie::Handle'; sub TIEHANDLE { my $class = shift; bless [ @_ ], $class } sub PRINT { my ($self,@vals) = @_; print $_ @vals for @$self } sub PRINTF { my ($self,@vals) = @_; printf $_ @vals for @$self }

      Just before pressing "create" here, I decided to search CPAN. Sure enough there is at least one module [Tie::FileHandle::MultiPlex] that implements almost exactly this idea. But I couldn't get it to work with my printf. I'm sure there are pitfalls with my quick hack at this too.