For tools like diff
that work with multiple files as parameters, it can be
useful to work with not just files on the filesystem, but also potentially with
the output of arbitrary commands. Say, for example, you wanted to compare the
output of ps
and ps -e
with diff -u
. An obvious way to do this is to
write files to compare the output:
$ ps > ps.out
$ ps -e > pse.out
$ diff -u ps.out pse.out
This works just fine, but Bash provides a shortcut in the form of process
substitution, allowing you to treat the standard output of commands as files.
This is done with the <()
and >()
operators. In our case, we want to direct
the standard output of two commands into place as files:
$ diff -u <(ps) <(ps -e)
This is functionally equivalent, except it’s a little tidier because it doesn’t
leave files lying around. This is also very handy for elegantly comparing files
across servers, using ssh
:
$ diff -u .bashrc <(ssh remote cat .bashrc)
Conversely, you can also use the >()
operator to direct from a filename
context to the standard input of a command. This is handy for setting up
in-place filters for things like logs. In the following example, I’m making a
call to rsync
, specifying that it should make a log of its actions in
log.txt
, but filter it through grep -vF .tmp
first to remove anything
matching the fixed string .tmp
:
$ rsync -arv --log-file=>(grep -vF .tmp >log.txt) src/ host::dst/
Combined with tee
this syntax is a way of simulating multiple filters for a
stdout
stream, transforming output from a command in as many ways as you see
fit:
$ ps -ef | tee >(awk '$1=="tom"' >toms-procs.txt) \
>(awk '$1=="root"' >roots-procs.txt) \
>(awk '$1!="httpd"' >not-apache-procs.txt) \
>(awk 'NR>1{print $1}' >pids-only.txt)
In general, the idea is that wherever on the command line you could specify a file to be read from or written to, you can instead use this syntax to make an implicit named pipe for the text stream.
Thanks to Reddit user Rhomboid for pointing out an incorrect assertion about
this syntax necessarily abstracting mkfifo
calls, which I’ve since
removed.