dan@rna.UUCP (Dan Ts'o) (01/14/85)
Hi, Recent I had reason to dig up an old mod I made to /bin/tee. Basically the mods allow tee to accept shell pipeline commands as descriptors to copy stdin to in addition to regular files. That is, $ tee file1 "|prog1 | prog2" file2 is possible, where the second argument is a shell pipeline which receives a copy of stdin as well as a file1 and file2. This property is useful when, for example, you want to view the data as its passing down the pipeline. I'm forever doing something like, $ prog1 | tee /dev/tty | prog2 to view the pipeline data. Now it is also possible to do, $ prog1 | tee "|more" | prog2 or $ plot | tee "|gd" | spool_plot_to_laser_printer where "gd" is the GPS graphics code dump program. Note that the stdout of each pipeline argument of this new tee is that of the tee command itself. Thus it is often desireable to redirect each pipeline's stdout explicitly, $ prog1 | tee "|more > /dev/tty" | prog2 so you don't have the pipeline output (if you expect any) to mix with "prog2"'s input. Below are the diffs for 4.2BSD's tee... Cheers, Dan Ts'o Dept. Neurobiology Rockefeller Univ. 1230 York Ave. NY, NY 10021 212-570-7671 ...cmcl2!rna!dan *** tee.c.org Sun Jan 13 21:33:12 1985 --- tee.c Sun Jan 13 22:01:29 1985 *************** *** 4,9 /* * tee-- pipe fitting */ #include <signal.h> #include <sys/types.h> --- 4,21 ----- /* * tee-- pipe fitting */ + /* + * tee [files] [shell commandlines] + * + * Modified 1/85 by D. Ts'o to create multiple pipelines by calling the + * shell upon arguments of the form + * "| pipeline" or + * "^ pipeline" + * The standard input of the new pipeline is the tee program while the + * standard output is the same as the tee program's standard output. + * Useful for branching off and filtering the same data several different + * ways. + */ #include <signal.h> #include <sys/types.h> *************** *** 45,50 if(lseek(1,0L,1)==-1&&errno==ESPIPE) t++; while(argc-->1) { if(aflag) { openf[n] = open(argv[1],1); if(openf[n] < 0) --- 57,70 ----- if(lseek(1,0L,1)==-1&&errno==ESPIPE) t++; while(argc-->1) { + /* + * dyt - do pipelines + */ + if(*argv[1] == '|' || *argv[1] == '^') { + if ((openf[n++] = mkpipe(argv[1])) < 0) + n--; + } + else { if(aflag) { openf[n] = open(argv[1],1); if(openf[n] < 0) *************** *** 62,67 n--; } argv++; } r = w = 0; for(;;) { --- 82,88 ----- n--; } argv++; + } } r = w = 0; for(;;) { *************** *** 97,100 { while(*s) write(2,s++,1); } --- 118,158 ----- { while(*s) write(2,s++,1); + } + + mkpipe(cmd) + char *cmd; + { + register int i; + int p[2]; + + if(pipe(p)) { + puts("tee: bad pipe call: "); + puts(cmd); + puts("\n"); + return(-1); + } + + if((i = fork()) == 0) { + close(0); + close(p[1]); + dup(p[0]); + close(p[0]); + cmd++; /* Skip over | character */ + execl("/bin/sh", "sh", "-c", cmd, 0); + puts("tee: no shell: "); + puts(cmd); + puts("\n"); + exit(-1); + } + + if(i == -1) { + puts("tee: bad fork call: "); + puts(cmd); + puts("\n"); + return(-1); + } + + close(p[0]); + return(p[1]); }
ajs@hpfcla.UUCP (ajs) (01/18/85)
> Basically the mods allow tee to accept shell pipeline commands as > descriptors to copy stdin to in addition to regular files... I hate to tell you this, but you didn't have to change tee to do it. (I hate to say so because (1) the proper solution is obscure and (2) I almost made the same changes myself before discovering it.) (And I'll tell you right here in net.sources, because this information just might prevent you from writing some unnecessary sources...) Anyway, awk(1) is already a very nice pipe splitter: awk '{ print | "first-pipeline"; print }' | second-pipeline You can do more than just split the data; you can also manipulate it in various wonderful ways as it goes through. You can set an awk variable to "first-pipeline", then reference it in several places: awk '{ ...; print "test" x | cmd; ...; print y | cmd; ...}' and only one instance of the pipeline will be created, to receive all the data in the order printed. You can even have awk write a shell script on the fly! How? Make cmd = "sh", and away you go. Why would you do that? Well, for one thing, it's a very nice way to, say, read a data file and mail various sections to various different people. (Hint: Use awk to pipe a shell script to "sh", which consists of a series of here-documents, surrounded by braces, piped to mail commands. For testing, set cmd = "cat > t" and look at file t afterwards to see what sh would have executed.) Awk: It's Not Just Another Ugly Program Alan Silverstein, Hewlett-Packard Fort Collins Systems Division, Colorado {ihnp4 | hplabs}!hpfcla!ajs, 303-226-3800 x3053, N 40 31'31" W 105 00'43"