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};
}