Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

IO::Tee and write / format

by beadon (Initiate)
on Oct 16, 2008 at 23:15 UTC ( [id://717623]=perlquestion: print w/replies, xml ) Need Help??

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;

Replies are listed 'Best First'.
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.

      I don't think you can use an IO::Tee object as a filehandle

      IO::Tee objects are (tied) file handles.

      > 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() ?
        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
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
      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 }
        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
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.

Re: IO::Tee and write / format
by salva (Canon) on Oct 17, 2008 at 08:42 UTC
    Try File::Tee. It forks a new process to handle the output instead of using a tied file handle that has several limitations as you have already found.

    The downside is that it doesn't work on Windows.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://717623]
Approved by Narveson
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others learning in the Monastery: (2)
As of 2024-04-25 20:13 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found