How can I send stdout to multiple commands?
You can use tee
and process substitution for this:
cat file.txt | tee >(pbcopy) | grep errors
This will send all the output of cat file.txt
to pbcopy
, and you'll only get the result of grep
on your console.
You can put multiple processes in the tee
part:
cat file.txt | tee >(pbcopy) >(do_stuff) >(do_more_stuff) | grep errors
You can specify multiple file names to tee
, and in addition the standard output can be piped into one command. To dispatch the output to multiple commands, you need to create multiple pipes and specify each of them as one output of tee
. There are several ways to do this.
Process substitution
If your shell is ksh93, bash or zsh, you can use process substitution. This is a way to pass a pipe to a command that expects a file name. The shell creates the pipe and passes a file name like /dev/fd/3
to the command. The number is the file descriptor that the pipe is connected to. Some unix variants do not support /dev/fd
; on these, a named pipe is used instead (see below).
tee >(command1) >(command2) | command3
File descriptors
In any POSIX shell, you can use multiple file descriptors explicitly. This requires a unix variant that supports /dev/fd
, since all but one of the outputs of tee
must be specified by name.
{ { { tee /dev/fd/3 /dev/fd/4 | command1 >&9;
} 3>&1 | command2 >&9;
} 4>&1 | command3 >&9;
} 9>&1
Named pipes
The most basic and portable method is to use named pipes. The downside is that you need to find a writable directory, create the pipes, and clean up afterwards.
tmp_dir=$(mktemp -d)
mkfifo "$tmp_dir/f1" "$tmp_dir/f2"
command1 <"$tmp_dir/f1" & pid1=$!
command2 <"$tmp_dir/f2" & pid2=$!
tee "$tmp_dir/f1" "$tmp_dir/f2" | command3
rm -rf "$tmp_dir"
wait $pid1 $pid2
If you are using zsh
then you can take advantage of the power of MULTIOS
feature, i.e. get rid of tee
command completely:
uname >file1 >file2
will just write the output of uname
to two different files: file1
and file2
, what is equivalent of uname | tee file1 >file2
Similarly redirection of standard inputs
wc -l <file1 <file2
is equivalent of cat file1 file2 | wc -l
(please note that this is not the same as wc -l file1 file2
, the later counts number of lines in each file separately).
Of course you can also use MULTIOS
to redirect output not to files but to other processes, using process substitution, e.g.:
echo abc > >(grep -o a) > >(tr b x) > >(sed 's/c/y/')