Can Perl redirect STDOUT to file AND to command window?

R

rcm228

I have done standard Perl STDOUT redirection to a pipe and to a file,
however, I have been searching for a way to redirect STDOUT to a file
and to the command window at the same time. So far, no luck - is this
possible?

Ryan
 
U

usenet

I have been searching for a way to redirect STDOUT to a file and to
the command window at the same time. So far, no luck - is this possible?

Sure. use IO::Tee (http://search.cpan.org/~kenshan/IO-Tee-0.64/Tee.pm)

Or, if you want to get REALLY cool, use Log::Dispatch and attach
handlers for file and screen (and you can fire the handlers based on
the log level of the message, so you can fire one or both handlers
based on the level you stipulate). I use Log::Dispatch for ALL of my
process messages (I rarely use "print" statements except for trivial
code).
 
S

Sherm Pendley

I have done standard Perl STDOUT redirection to a pipe and to a file,
however, I have been searching for a way to redirect STDOUT to a file
and to the command window at the same time. So far, no luck - is this
possible?

If you're on a *nix, "man tee".

sherm--
 
R

rcm228

Unfortunately, I am on windows (else I wouldnt be here ;)

My situation is a bit complex and requires me to do all the redirection
within the script, so I can't get away with this either:

perl my_script.pl | tee out.txt

R
 
U

usenet

Or, if you want to get REALLY cool, use Log::Dispatch and attach
handlers for file and screen...

Here's an example of how to do this. Log::Dispatch is really a great
way to handle process messages (and you can attach other methods if you
wish - it can send you e-mail, or log to XML files or to databases)...

#!/usr/bin/perl
use strict; use warnings;

use Log::Dispatch::Screen;
use Log::Dispatch::File;

my $log = Log::Dispatch -> new; #This is the logger

#Logging method for terminal output. Fires at 'debug' or higher
$log ->add( Log::Dispatch::Screen ->new(
name => 'screen',
min_level => 'debug',
stderr => 0, )
);
#Logging method for file output. Fires at 'info' or higher
$log ->add( Log::Dispatch::File ->new(
name => 'file',
min_level => 'info',
filename => '/tmp/whatever.log',
mode => 'append' )
);

### Now the logging methods are set up. We can invoke the logger
### by passing it a message at a particular urgency level, namely:
### debug info notice warning error critical alert emergency
### The logger will fire ANY and ALL methods which are defined as
### equal to or less than the urgency level we stipulate.

$log -> debug("This message goes to the screen only\n");
$log -> info("This message goes to BOTH screen and file.\n");

$log -> log_to(name => 'file',
level => 'info',
message => "Force message to go to file ONLY\n");

__END__

# Never use 'print()' again!
 
U

usenet

Here's an example of how to do this. Log::Dispatch is really a great
way to handle process messages...

But, wait, there's more! If you use the Log::Dispatch module in the
next 30 minutes, you will get callback capability at NO ADDITIONAL
COST! That's right - a billion dollar value is yours FREE!

Are you tired of having to fuss with linefeeds on your output messages?
Define a callback! Do you want to timestamp each line that you write
to your logfile (but not bother to show the timestamp on STDOUT
messages? Define a callback! For example:

my $add_lf = sub { my %p = @_; "$p{'message'}\n"};
my $add_timestamp = sub { my %p = @_;
sprintf "%s - %s", scalar(localtime),
$p{'message'}; };

OK, now you've defined two callbacks (one to add linefeeds, one to add
timestamps). Assuming that you want to add linefeeds to every single
thing that you output, you would define your logger to always use the
$add_lf callback:

$log = Log::Dispatch->new ( callbacks => $add_lf );

But you only want to call the timestamp-er on the 'file' method, so you
would define that method like this:
$log ->add( Log::Dispatch::File ->new(
name => 'file',
min_level => 'info',
filename => '/tmp/whatever.log',
callbacks => $add_timestamp,
mode => 'append' )
);

Now you can log even multiple lines, such as this:

$log->debug('line 1', 'line 2', 'line 3');

And it will automatically add linefeeds to each line, and it will
timestamp each line (but only in the file, not on the terminal).

Call CPAN now - webservers are standing by to take your order!
 
R

rcm228

Thanks for the start, IO::Tee works well like this:

use strict;
use IO::Tee;

my $tee = IO::Tee->new(\*STDOUT,">out.txt");
print $tee "test\n";

However, I am looking for something like this where I dont need to
print to the $tee handle:

use strict;
use IO::Tee;

my $tee = IO::Tee->new(\*STDOUT,">out.txt");
open(STDOUT, ">&$tee");
print "test\n";

This creates out.txt and prints "test" to the screen but not to out.txt

Do I need to use tie() ?

tie(*STDOUT, "mypackage");

R
 
U

usenet

However, I am looking for something like this where I dont need to
print to the $tee handle:

Hmmm. I don't know if you can do this. 'print' (by default) goes to
STDOUT. But you want to redefine STDOUT as a tee that goes to a file
and... STDOUT. But STDOUT is now a tee that goes to a file and...
STDOUT. This goes on forever.
 
R

rcm228

Yeah...I got stuck in that loop myself, my watchdog kicked....bummer, I
guess the consensus for this thread is "no" unless you want to be
explicit about the handle.

Thanks for the help...

R
 
X

xhoster

Thanks for the start, IO::Tee works well like this:

use strict;
use IO::Tee;

my $tee = IO::Tee->new(\*STDOUT,">out.txt");
print $tee "test\n";

However, I am looking for something like this where I dont need to
print to the $tee handle:

How about just selecting $tee so that it becomes the default output handle?

select $tee;
use strict;
use IO::Tee;

my $tee = IO::Tee->new(\*STDOUT,">out.txt");
open(STDOUT, ">&$tee");

Always check the success of your open.

Xho
 
R

rcm228

PG,

I don't want to go into too much gory detail, but basically my module
is for concurrent scripting with a simple IPC interface that works via
C++, users of the module don't know that I am redirecting their output
to a file and to the cmd window ;)

R
 
R

rcm228

**** SUMMARY **** (for those on the Google quest)

use strict;
use IO::Tee;

my $tee = IO::Tee->new(\*STDOUT,">out.txt");
select $tee;

print "Hello World\n"; # will print Hello World to both cmd window and
../out.txt

R
 
A

Anno Siegel

Hmmm. I don't know if you can do this. 'print' (by default) goes to
STDOUT.

It goes to the filehandle chosen by select(). The default is STDOUT.
But you want to redefine STDOUT as a tee that goes to a file
and... STDOUT. But STDOUT is now a tee that goes to a file and...
STDOUT. This goes on forever.

One could tie STDOUT to something that writes to the file and to
what STDOUT used to be before the tie. Using the IO::Tee module:

use IO::Tee;

my $file;
open $file, '>', $_ or die "Can't create '$_': $!" for '/tmp/x';
my $tee = IO::Tee->new( \ *STDOUT, $file);
select $tee;

print "$_\n" for qw( hihi haha hoho);

Anno
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,023
Latest member
websitedesig25

Latest Threads

Top