[comp.unix.questions] tee like program to pipe to another program?

pete@othello.dartmouth.edu (Pete Schmitt) (06/06/91)

Is there a tee like program that will pipe down to another program?

-pete schmitt
--
     Peter Schmitt                                          UNIX Consultant
Kiewit Computation Center                                Computing  Services
   Dartmouth College                                         (603)646-2085
   Hanover, NH 03755                                 Peter.Schmitt@Dartmouth.EDU

dkeisen@leland.Stanford.EDU (Dave Eisen) (06/06/91)

In article <1991Jun6.093939.9346@dartvax.dartmouth.edu> pete@othello.dartmouth.edu (Pete Schmitt) writes:
>Is there a tee like program that will pipe down to another program?
>

Sure. It's called "tee".

Something like:

program1 | tee /dev/tty | program2

should do what you want.


-- 
Dave Eisen                           dkeisen@leland.Stanford.EDU
1101 San Antonio Road, Suite 102     
Mountain View, CA 94043
(415) 967-5644

Tom Christiansen <tchrist@convex.COM> (06/06/91)

(Put followups wherever you want -- I just didn't want them to 
land in alt.sources unless they were also source.)

From the keyboard of dkeisen@leland.Stanford.EDU (Dave Eisen):
:In article <1991Jun6.093939.9346@dartvax.dartmouth.edu> pete@othello.dartmouth.edu (Pete Schmitt) writes:
:>Is there a tee like program that will pipe down to another program?
:>
:
:Sure. It's called "tee".
:
:Something like:
:
:program1 | tee /dev/tty | program2
:
:should do what you want.

I suspect that this is not want the original poster wanted.  While its
true that it does work in this case, it's not going to work if you want
to "tee off" to a list of processes.

Here's a program that's a supersets of the original tee, so I think
you can just put it in your own bin and call it tee; I did..  

Instead of just file specs, you can give pipe specs like this "|program".
So for the simple suggestion above, the usage would be

    program1 | tee "|program2"

which isn't particularly interesting.  This is:

    program1 | tee "|program2" "|program3" "|program4"

It still understands -a for append and -i for ignoring interrupts
(which I've never used), as well as a new -u for "unbuffered" output,
especially useful with pipes.  You can also mix your appends and
overwrites by specifying ">>file" for appending.  "file" is the same as
">file", unless the -a flag is on, in which case it's ">>file".  
You can always use ">file" or ">>file" to override the default.
For example, not using any defaults:

$ prog1 | tee -u ">file1" "|prog2" ">>file2" "|prog3 | prog4" ">file3" ">>file4"

prog1 runs into tee, which duplicates its output to several different
places.  first, a copy goes to stdout (redirect into /dev/null if you
don't want this.) file1 and file3 get overwritten, file2 and file4 get
appended to, and prog2 and prog3 get run.  oh, and prog3 runs into prog4.

Program follows; not bad for ~30 lines of code, eh? :-)

--tom

#!/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);
    die "usage tee [-aiu] [filenames] ...\n";
} 
if ($ignore_ints) { 
    for $sig ('INT', 'TERM', 'HUP', 'QUIT') { $SIG{$sig} = 'IGNORE'; } 
}
$mode = $append ? '>>' : '>';
$fh = 'FH000';
%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) { 
    close($fh) && next;
    warn "$0: couldn't close $fh{$fh}: $!\n";
    $status++;
}
exit $status;
--
Tom Christiansen		tchrist@convex.com	convex!tchrist
	    "Perl is to sed as C is to assembly language."  -me

tchrist@convex.COM (Tom Christiansen) (06/07/91)

It might be nice to get better than an ungraceful death by
SIGPIPE if a pipe kid dies or never gets started.  Here's
a patch to trap this:

16a17
> $SIG{'PIPE'} = 'PLUMBER';
37c38
<     close($fh) && next;
---
>     next if close($fh) || !defined $fh{$fh};
41a43,48
>
> sub PLUMBER {
>     warn "$0: pipe to \"$fh{$fh}\" broke!\n";
>     $status++;
>     delete $fh{$fh};
> }


--tom
--
Tom Christiansen		tchrist@convex.com	convex!tchrist
	    "Perl is to sed as C is to assembly language."  -me

pd@pd@x.co.uk (Paul Davey) (06/07/91)

>>>>> On 6 Jun 91 09:39:39 GMT, pete@othello.dartmouth.edu (Pete Schmitt) said:

Pete> Is there a tee like program that will pipe down to another program?

You can get the effect of this on some machines by using tee and a
named pipe or fifo file. (This is not a general solution.)

to pipe the output from lotsofdataprogram to stream1 and stream2,


mknod fifo p
lotsofdataprogram | tee fifo | stream1 ... &
cat fifo | stream2 ...


Don't forget to rm fifo when done with it.

--
 Regards,			 pd@x.co.uk          IXI Limited
	Paul Davey		 pd@ixi.uucp         62-74 Burleigh St.
				 ...!uunet!ixi!pd    Cambridge  U.K.
 "These are interesting times"   +44 223 462 131     CB1  1OJ      
				 USA: 1 800 XDESK 57

ruba@molbio.ethz.ch (Rudolf Baumann) (06/08/91)

pete@othello.dartmouth.edu (Pete Schmitt) writes:

>Is there a tee like program that will pipe down to another program?

I am using tpipe from David Rosen which once appeared in comp.sources.unix:

%Newsgroups: comp.sources.unix
%Subject: v22i011:  A "tee" that feeds into pipes.
%Message-ID: <2484@papaya.bbn.com>
%Date: 3 May 90 20:43:46 GMT
%Approved: rsalz@uunet.UU.NET
%
%Submitted-by: David B Rosen <rosen@bucasb.bu.edu>
%Posting-number: Volume 22, Issue 11
%Archive-name: tpipe
%
%tpipe is a simple utility program that can be used to split a unix
%pipeline into two pipelines. That is, the output of one pipeline can
%be replicated and supplied as the input to two other pipelines
%executing simultaneously.
%
%For example
%    cmd1 <infile | tpipe "cmd2 | cmd3 >outfile" | cmd4
%will look like this diagramatically:
%
%                         --> cmd2 --> cmd3 --> outfile
%                       /
%    infile  --> cmd1 -<
%                       \\
%                         --> cmd4 -->  (standard output)

That is probably what you are looking for.
	Ruedi
--
Rudolf E. Baumann                                    ruba@molbio.ethz.ch
Institut fuer Molekularbiologie & Biophysik          MOLEKULA@CZHETH5A.bitnet
ETH Hoenggerberg (HPM G6)                            Tel. ++41 1 377 33 97
CH-8093 Zuerich/Switzerland                          Fax  ++41 1 371 48 73

renkel@motcid.UUCP (Will Renkel) (06/10/91)

In article <PD.91Jun7133525@powys.x.co.uk>, pd@pd@x.co.uk (Paul Davey) writes:
> 
> >>>>> On 6 Jun 91 09:39:39 GMT, pete@othello.dartmouth.edu (Pete Schmitt) said:
> 
> Pete> Is there a tee like program that will pipe down to another program?
> 
> You can get the effect of this on some machines by using tee and a
> named pipe or fifo file. (This is not a general solution.)
> 
> to pipe the output from lotsofdataprogram to stream1 and stream2,
> 
> 
> mknod fifo p
> lotsofdataprogram | tee fifo | stream1 ... &
> cat fifo | stream2 ...
> 
> 
> Don't forget to rm fifo when done with it.
> 
> --
>  Regards,			 pd@x.co.uk          IXI Limited
> 	Paul Davey		 pd@ixi.uucp         62-74 Burleigh St.
> 				 ...!uunet!ixi!pd    Cambridge  U.K.
>  "These are interesting times"   +44 223 462 131     CB1  1OJ      
> 				 USA: 1 800 XDESK 57


Got this code while working at ATT Bell Labs
I call it "branch" and its use is like the following

echo HI | branch 'grep HI | grep HI >stream1' | while read p1
do
echo $p1
done | tee stream2


--------- CUT HERE ----------------.
#include <stdio.h>

/* Similiar to the shell command "tee", but the standard input
 * is copied into two different pipelines at the same time
 *J. Leth, IH 6E-318 x6613.  */

#define STRLEN 255
#define errexit(x,arg)  fprintf(stderr, x, arg); exit(1)

main(argc, argv)
int argc;
char *argv[];
     {
     char *command;
     int fildes[2];  /* file descriptors for created pipe
     * fildes[0] is reader, fildes[1] is writer */
     if (argc != 2)
          {
          printf("\n\nUsage:\tbranch <command>\n");
          printf("Copies standard input to standard output, and\n");
          printf("at the same time copies it to the given command.\n");
          printf("The Command may itself be a pipeline (be careful\n");
          printf("to enclose it in quotes).\n");
          printf("example - cat abc | branch \"grep tss >temp1\" | grep qqq\n");
          errexit("\n\n\n",1);
          }
     command = argv[1];
          if (pipe(fildes) == -1) {
          errexit("branch: can't get new pipe\n",0);
          }
     switch (fork())
          {
          case -1:
          errexit("branch: can't fork\n",0);
          case 0:
          /* brancher process -- Execute command with standard input
          * taken from pipe from forker process. */
          close(fildes[1]);        /* close writer */
          
          /* Duplicate pipe reader as standard input */
          close(0);
          dup(fildes[0]);
          execl("/bin/sh", "/bin/sh", "-c", command, 0);
          default:
               {       /* Forker process */
               int c;
               FILE *pipewrt;
               close(fildes[0]);       /* close reader */
               pipewrt = fdopen(fildes[1], "w");
               setbuf(pipewrt, NULL);
                    while(!feof(stdin) && !ferror(stdin)) {
                    putchar(c = getchar());
                    putc(c, pipewrt);
                    }
               fclose(pipewrt);
               fclose(stdin);
               exit(0);
               }
          }
     }