home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  

Unix Power ToolsUnix Power ToolsSearch this book

43.10. Redirecting Output to More Than One Place

What if you want to use the output of a program more than once, and you don't want to deal with an intermediary file? For example, suppose I have some large, compressed PostScript files. I want to print the files, but I also want to know how many pages they are. I know that the number of pages appears on a line following %%Pages: at the end of the file. Using bzcat (Section 15.6) to uncompress the file to standard output, I can type the following commands into a for loop (Section 28.9) (or put them into a shell script). This loop sends each file to the printer and uses sed to capture the correct line:

-n Section 34.3

for f
    bzcat $f | lpr
    bzcat $f | sed -n "s/^%%Pages: \([0-9][0-9]*\)/$f:   \1 pages/p"

But this ends up running bzcat twice, which takes some time. I can expand the file with bunzip2 first, but frankly I'm not sure I have the disk space for that.

Using process substitution and tee (Section 43.8), I can do it in one line, without wasting processes and without eating disk space:

for f
  bzcat $f | tee >(lpr) | sed -n "s/^%%Pages: \([0-9][0-9]*\)/$f: \1 pages/p"

From running this script, as each file is sent to the printer I receive the following messages on my screen:

ch01.ps.gz: 44 pages
ch02.ps.gz: 51 pages
ch03.ps.gz: 23 pages

Because tee can write to more than one file, it can write to more than one process with process substitution. For instance, maybe you want to send the file to both a black-and-white printer and a color printer at the same time:

bzcat $f | tee >(lpr -Pbw) >(lpr -Pcolor) | \
  sed -n "s/^%%Pages: \([0-9][0-9]*\)/$f: \1 pages/p"

Figure Go to http://examples.oreilly.com/upt3 for more information on: tpipe

If your shell doesn't have process substitution, maybe you have a shell like bash or zsh that does. (Write a shell script. Or type the shell's name at your shell prompt, then type exit when you're done with the temporary shell.) Otherwise, you can use tpipe; it's on the CD-ROM [see http://examples.oreilly.com/upt3]. tpipe is similar to tee (Section 43.8), but instead of putting a copy of standard input in a file, it passes the input to a new pipe. Give tpipe the name of the command (here, lpr) that will read the text from its standard input:

bzcat $f | tpipe lpr | sed -n "s/^%%Pages: \([0-9][0-9]*\)/$f: \1 pages/p"

You can also simulate tpipe by using awk (Section 20.10). Write a little awk script that reads each input line and writes the text both to a command and to awk's standard output:

bzcat $f | awk "{ print | \"lpr\" ; print }" | \
  sed -n "s/^%%Pages: \([0-9][0-9]*\)/$f:   \1 pages/p"

This is much slower and only works on text files, but it does the job.

--LM and JP

Library Navigation Links

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