1: Using braces and file descriptors to group input and output -------------------------------------------------------------- The braces '{}' can be used to control the output (and input) of a group of commands. Use one of the following methods to copy a stream of data to two different locations. Streams of data are accessible in shell as files or using file descriptors (FDs) You can duplicate any stream of data (option 1), or you can read a plain file as many times as you like (option 2). Option #1: ========== By using "tee", we can create two (or more) output streams from an input stream. With this example, you can handle STDIN from a pipe, a fifo, a plain file or a socket. Any source of input can be sent two different directions (in the case below, one to "mail" and the other into a file called $OUTPUT). { # -- copy STDIN to STDOUT and FD 3 simultaneously # tee /dev/fd/3 \ | mail -s "$SUBJECT" $RECIP ; # # -- handle output from FD 3 outside the braces. # } < $INPUT > $OUTPUT 3>&1 Option #2: ========== Bash allows special handling of /dev/stdin, which means that you can rewind STDIN, effectively reading the same input file more than once. There is a big danger here! If input is not a regular file, the file descriptor rewinding ("exec < /dev/stdin") will fail. The moral of that story is that you may not seek on a pipe. { # -- send a mail message, the body will be the contents of the # input file # mail -s "$SUBJECT" $RECIP # # -- rewind STDIN, so that we can start all over at the beginning # of the input file # exec < /dev/stdin cat > $OUTPUT ; } < $INPUT Several tricks with tee, file descriptors and exec. + Make several copies of a file simultaneously. $ tee copy-0 copy-1 copy-2 > copy-3 < original + Open a script itself on its STDIN*. (A script's full pathname is available as the positional parameter $0.) By convention, UN*X-heads omit the file descriptor where it is implied. The following are functionally the same: implicit FD reference explicit FD reference --------------------- --------------------- #! /bin/bash #! /bin/bash exec <"$0" ; cat - exec 0<"$0" ; cat - + Throw away your own STDERR. #! /bin/bash exec 2>/dev/null + Throw away your own STDOUT. Remember that the explicit FD reference is not required, and is commonly omitted. implicit FD reference explicit FD reference --------------------- --------------------- #! /bin/bash #! /bin/bash exec >/dev/null exec 1>/dev/null + Squirrel away your STDIN until later. Note that though this example shows how to swap file descriptors, the work done is trivial and does not require file descriptor swapping. #! /bin/bash # # MYFILENAME="$0" exec 7<&0 # -- save the STDIN FD given by parent exec 0< $MYFILENAME # -- open a new file connected to STDIN cat - # -- do something with STDIN ($MYFILENAME) exec 0<&- # -- close STDIN exec 0<&7 # -- put original STDIN back where it was cat - # -- spit out contents of STDIN from parent