[LWN Logo]

From: Tom Christiansen <tchrist@mox.perl.com>
Subject: SRC: printing to more than one filehandle
Date: 22 May 1998 13:50:20 GMT

Ever want one filehandle to connect to more than one file?
You could do this:

    for $fh (*FH1, *FH2, *FH3)   { print $fh "whatever\n" }

or you could do this:

    open(MANY, "| tee file1 file2 file3 > /dev/null")   || die $!;
    print MANY "data\n"                                 or die $!;
    close(MANY)                                         || die $!;

Or you could use a tied filehandle:

    use Tie::Tee;
    use Symbol;

    @handles = (*STDOUT);
    for $i ( 1 .. 10 ) {
        push(@handles, $handle = gensym());
        open($handle, ">/tmp/teetest.$i");
    } 

    tie *TEE, 'Tie::Tee', @handles;
    print TEE "This lines goes many places.\n";

Using this module:

    package Tie::Tee;

    sub TIEHANDLE {
        my $class   = shift;
        my $handles = [@_];

        bless $handles, $class;
        return $handles;
    }

    sub PRINT {
        my $href = shift;
        my $handle;
        my $success = 0;

        foreach $handle (@$href) {
            $success += print $handle @_;
        }

        return $success == @$href;
    }                                     

    1;

Or you could have your own tee program, which is more powerful than
the regular one, 

    $ command | tctee file1 ">>file2" "|cmd" file3 "|cmd3" file4

using the tctee program below:

    #!/usr/bin/perl
    # tee clone that groks process tees (should work even with old perls)
    # Tom Christiansen <tchrist@convex.com>
    # 6 June 91

    while ($ARGV[0] =~ /^-(.+)/ && (shift, ($_ = $1), 1)) {
	next if /^$/;
	s/i// && (++$ignore_ints, redo); 
	s/a// && (++$append,      redo);
	s/u// && (++$unbuffer,    redo);
	s/n// && (++$nostdout,    redo);
	die "usage tee [-aiun] [filenames] ...\n";
    } 
    if ($ignore_ints) {
	for $sig ('INT', 'TERM', 'HUP', 'QUIT') { $SIG{$sig} = 'IGNORE'; } 
    }
    $SIG{'PIPE'} = 'PLUMBER';
    $mode = $append ? '>>' : '>';
    $fh = 'FH000';
    unless ($nostdout) { 
	%fh = ('STDOUT', 'standard output'); # always go to stdout
    }
    $| = 1 if $unbuffer;

    for (@ARGV) {
	if (!open($fh, (/^[^>|]/ && $mode) . $_)) {
	    warn "$0: cannot open $_: $!\n"; # like sun's; i prefer die
	    $status++;
	    next;
	}
	select((select($fh), $| = 1)[0]) if $unbuffer;
	$fh{$fh++} = $_;
    } 
    while (<STDIN>) {
	for $fh (keys %fh) {
	    print $fh $_;
	} 
    } 
    for $fh (keys %fh) { 
	next if close($fh) || !defined $fh{$fh};
	warn "$0: couldn't close $fh{$fh}: $!\n";
	$status++;
    }
    exit $status;

    sub PLUMBER {
	warn "$0: pipe to \"$fh{$fh}\" broke!\n";
	$status++;
	delete $fh{$fh};
    } 

-- 
MS-DOS is CP/M on steroids, bigger bulkier and not much better.
Windows is MS-DOS with a bad copy of a Macintosh GUI.
NT is a Windows riddled with VMS.