Program: tctee (Perl Cookbook, 2nd Edition)

## 8.25. Program: tctee

Not all systems support the classic tee program for splitting output pipes to multiple destinations. This command sends the output from someprog to /tmp/output and to the mail pipe beyond:

>     % someprog | tee /tmp/output | Mail -s "check this" user@host.org

This program helps not only users who aren't on Unix systems and don't have a regular tee; it also helps those who are, because it offers features not found on other versions of tee.

The four flag arguments are -i to ignore interrupts, -a to append to output files, -u for unbuffered output, and -n to omit copying the output on to standard out.

Because this program uses Perl's magic `open`, you can specify pipes as well as files.

>     % someprog | tctee f1 "|cat -n" f2 ">>f3"

That sends the output from someprog to the files f1 and f2, appends it to f3, sends a copy to the program cat -n, and also produces the stream on standard output.

The program in Example 8-8 is one of many venerable Perl programs written nearly a decade ago that still runs perfectly well. If written from scratch now, we'd probably `use` `strict`, warnings, and ten to thirty thousand lines of modules. But if it ain't broke . . .

#### Example 8-8. tctee

>       #!/usr/bin/perl
>       # tctee - clone that groks process tees
>       # perl3 compatible, or better.

>       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 $0 [-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: couldnt close $fh{$fh}: $!\n";
>           $status++;
>       }

>       exit $status;

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

Copyright © 2003 O'Reilly & Associates. All rights reserved.