beadon has asked for the wisdom of the Perl Monks concerning the following question:
I expect the following code to produce output to the $tee filehandle, which is IO::Tee. Why is nothing coming out ?
use strict;
use IO::Tee;
my $email_body;
open BODY , ">", \$email_body or die "Unable to store to a var : $!\n"
+;
my $tee = IO::Tee->new (\*STDOUT,\*BODY); # create a new tee object
+and attach STDOUT and the email buffer to it.
$tee->autoflush(1);
my ( $check_description,$check_result,$severity_w );
my $VERSION = 10.0;
my $host = "test";
format HEADER =
____v@<<<< @<<<<<<<<<<<_______________________________________________
+______________________________________________
$VERSION, $host
Description
+ Check Result - Status-Severity
______________________________________________________________________
+______________________________________________
.
format FAIL =
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>>>>>>>>>>>>>>>>>>
+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -ERROR-@>
$check_description,$check_result,$severity_w
.
format STD =
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>>>>>>>>>>>>>>>>>>
+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
$check_description,$check_result
.
select ($tee); # select the default FH
$^ = 'HEADER'; # set the top of page format
$~ = 'FAIL';
$check_description = "desc";
$check_result = "result";
$severity_w = "9";
write;
Re: IO::Tee and write / format
by Illuminatus (Curate) on Oct 17, 2008 at 02:12 UTC
|
I don't think you can use an IO::Tee object as a filehandle, but you can invoke most IO::Handle and IO::File methods with it. Take a closer look at IO:Tee, and look at IO::Handle. | [reply] |
|
| [reply] |
|
> perl -MIO::Tee -e 'my $tee = IO::Tee->new(\*STDOUT,\*STDERR); print
+$tee "Test\n";'
Test
Test
> perl -MIO::Tee -e 'my $tee = IO::Tee->new(\*STDOUT,\*STDERR,\*STDOUT
+,\*STDERR); printf $tee "Test\n";'
Test
Test
Test
Test
seems to work, right ? Is something wrong with how I've used write() ? | [reply] [d/l] |
|
I thought the problem was the writing to a file handle bound to a string ref (\*BODY), so I tried it with IO::String, but it still didn't work:
#!/usr/bin/perl
use strict;
use IO::Tee;
use IO::String;
my $email_body;
my $tee = IO::Tee->new(\*STDOUT,new IO::String($email_body)); # crea
+te a new tee object and attach STDOUT and the email$
die $! unless $tee;
$tee->autoflush(1);
my ( $check_description,$check_result,$severity_w );
my $VERSION = 10.0;
my $host = "test";
format HEADER =
____v@<<<< @<<<<<<<<<<<_______________________________________________
+______________________________________________
$VERSION, $host
Description
+ Check Result - Status-Severity
______________________________________________________________________
+______________________________________________
.
format FAIL =
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>>>>>>>>>>>>>>>>>>
+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -ERROR-@>
$check_description,$check_result,$severity_w
.
format STD =
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>>>>>>>>>>>>>>>>>>
+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
$check_description,$check_result
.
select STDOUT;
$^ = 'HEADER'; # set the top of page format
$~ = 'STD';
$check_description = "desc";
$check_result = "result";
$severity_w = "9";
write;
print "EMAIL BODY:\n\n$email_body\n";
exit;
Had to learn something about formats, I think everything is correct except IO::Tee doesn't handle a string filehandle properly. The formats output fine if selecting STDOUT.
SSF
| [reply] [d/l] |
Re: IO::Tee and write / format
by broomduster (Priest) on Oct 17, 2008 at 13:24 UTC
|
You need the format_write and format_top_name methods (and maybe others, depending on how sophisticated your form handling needs to be). IO::Tee inherits some format-handling code from IO::Handle. The docs for IO::Tee really only mention it in passing, but it becomes clearer in the docs for IO::Handle, and you need to be sure to tell IO::Tee specifically about things like which format to use for top-of-form (which is "automatic" for STDOUT and the like).
The following code snippet should get you started. Notice that since the name of the format is passed down to IO::Tee, you need to specify the full package name of the format (o/wise you get complaints about undefined formats from IO::Tee).
use strict;
use warnings;
use IO::Tee;
my( $abc, $xyz ) = ( q/hello/, 12345 );
format TEE_TOP =
==== Top of Output ====
.
format TEE =
@<<<<< @#####
$abc, $xyz
.
# with lexical file handle; can also use bareword handle
open my $outfile, '>', 'junk.out' or die "error opening junk.out: ($!
+)";
my $tee = IO::Tee->new( \*STDOUT, $outfile );
# see docs for IO::Tee and IO::Handle for explanation of these methods
# and others available for proper format use/control
$tee->format_top_name( q/main::TEE_TOP/ );
$tee->format_write( q/main::TEE/ );
From the shell:
-> ./tst-tee-write
==== Top of Output ====
hello 12345
-> cat junk.out
==== Top of Output ====
hello 12345
| [reply] [d/l] [select] |
|
wow, I see it now. Thanks for that huge tip.
There seems to be some problem when setting it just for the $tee though, write() seems to expect $~ and $^ to be filled in too , otherwise it keeps trying to use the name of the filehandle IO::<something> which obviously won't work.
After setting $^ and $~ properly before write() then write began spitting out duplicate lines each time it was called. With each successive call to write, write() would output the previous line and the current line.
to solve this I cleared the accumulator ( $^A = '' ) after writing each line. That solved the problem.
sub print_clean_stdout {
my $result = shift;
$check_description = shift;
$check_result = shift;
$severity_w = shift;
if ( $result eq 'FAIL' ) {
$tee->format_write( q/main::FAIL/ );
$~ = 'FAIL';
$failures++;
}
else {
$tee->format_write( q/main::STD/ );
$~ = 'STD';
$successes++;
}
write;
$^A = ''; # we have to mess with the accumulator
}
| [reply] [d/l] |
|
The whole point here is to use $tee->format_write() instead of write. Then you don't have to fool around with $^ et al. (it all happens automagically). A better way for me to have shown this might have been with more calls to $tee->format_write(), as in the following modified version of what I posted earlier.
use strict;
use warnings;
use IO::Tee;
my %hash = map { ("key$_", "val$_") } ( 1 .. 5 );
my $key;
format TEE_TOP =
==== Top of Output ====
.
format TEE =
@<<<<< @<<<<<
$key, $hash{$key}
.
# with lexical file handle; can also use bareword handle
open my $outfile, '>', 'junk.out' or die "error opening junk.out: ($!
+)";
my $tee = IO::Tee->new( \*STDOUT, $outfile );
# see docs for IO::Tee and IO::Handle for explanation of these methods
# and others available for proper format use/control
$tee->format_top_name( q/main::TEE_TOP/ );
for $key ( sort keys %hash ) {
$tee->format_write( q/main::TEE/ );
}
from the shell:
-> ./tst-tee-write
==== Top of Output ====
key1 val1
key2 val2
key3 val3
key4 val4
key5 val5
-> cat junk.out
==== Top of Output ====
key1 val1
key2 val2
key3 val3
key4 val4
key5 val5
| [reply] [d/l] [select] |
Re: IO::Tee and write / format
by ikegami (Patriarch) on Oct 17, 2008 at 06:34 UTC
|
My initial and still current guess is that formats don't work on tied handles.
Have you considered Perl6::Form (Perl6-style forms for Perl5) as an alternative? I heard good things about it.
| [reply] |
Re: IO::Tee and write / format
by salva (Canon) on Oct 17, 2008 at 08:42 UTC
|
| [reply] |
|
|