What exactly is <() in bash (and =() in zsh)?

This is called process substitution.

The <(list) syntax is supported by both, bash and zsh. It provides a way to pass the output of a command (list) to another command when using a pipe (|) is not possible. For example when a command just does not support input from STDIN or you need the output of multiple commands:

diff <(ls dirA) <(ls dirB)

<(list) connects the output of list with a file in /dev/fd, if supported by the system, otherwise a named pipe (FIFO) is used (which also depends on support by the system; neither manual says what happens if both mechanisms are not supported, presumably it aborts with an error). The name of the file is then passed as argument on the command line.


zsh additionally supports =(list) as possible replacement for <(list). With =(list) a temporary file is used instead of file in /dev/fd or a FIFO. It can be used as a replacement for <(list) if the program needs to lseek in the output.

According to the ZSH manual there might also be other issues with how <(list) works:

The = form is useful as both the /dev/fd and the named pipe implementation of <(...) have drawbacks. In the former case, some programmes may automatically close the file descriptor in question before examining the file on the command line, particularly if this is necessary for security reasons such as when the programme is running setuid. In the second case, if the programme does not actually open the file, the subshell attempting to read from or write to the pipe will (in a typical implementation, different operating systems may have different behaviour) block for ever and have to be killed explicitly. In both cases, the shell actually supplies the information using a pipe, so that programmes that expect to lseek (see man page lseek(2)) on the file will not work.


Note, this is a bash answer, not zsh.

There are cases in bash where you can't use pipes:

some_command | some_other_command

because pipes introduce subshells for each component of the pipeline, when the subshells exit, any side-effects you were relying on would disappear. For example, this contrived example:

cat file | while read line; do ((count++)); done
echo $count

will display a blank line, because the $count variable does not exist in the current shell.

A bash process substitution allows you to avoid this conundrum by allowing you to read from the "some_command" output like you would from a file

while read line; do ((count++)); done < <(cat file)
# ....................................1.2
echo $count   # the variable *does* exist in the current shell

(1) is a normal input redirection. (2) is the start of the <() process substitution syntax.