A little useful trick for making the input / output of a command act as a temporary file into a larger command.

Syntax

<(command)

or

>(command)

The shell secretly creates a pipe/FIFO or /dev/fd/* file descriptor behind the scenes, then substitutes that fake filename into the command.

<(...) = “pretend this command output is a file” >(...) = “pretend this command input is a file”

This is most useful when a command requires two files (or filenames), and doesn’t allow for stdin input.

Example usage

The diff example (Input substitution)

An example is the ‘diff’ command:

diff file1 file2

The diff command requires file inputs:

 diff --help
Usage: diff [OPTION]... FILES
...
FILES are 'FILE1 FILE2' or 'DIR1 DIR2' or 'DIR FILE' or 'FILE DIR'.

A common example for this use case is if you have two text files, and you need to sort them, and then get the difference. A quick way to do that would be:

diff <(sort a.txt) <(sort b.txt)

Bash creates temporary file descriptors such as /dev/fd/88 or /dev/fd/89.

The above example would then be roughly equivalent to:

diff /dev/fd/88 /dev/fd/89`

which allows diff to act like it received files.

The tee example (Output substitution)

Example:

echo hello | tee >(wc -c)

In this instance, tee duplicates the output, where one copy goes to the terminal, and the other goes to wc -c

So the resulting output would be:

hello
6

Useful for logging, checksums, multiple outputs, etc.

The double bracket example

Posts like MLG_Sinon’s example and Geirha’s example both show cases where you can use a < < to read lines of a command’s output.

Take this example:

find . | while read x; do
    count=$((count+1))
done

In Bash, that loop may run in a subshell, so count disappears afterward.

while read x; do
    count=$((count+1))
done < <(find .)

Now the loop runs in the current shell.

As Geirha explains: “Each part of a pipeline runs in separate subshells, and any variables modified in a subshell are gone after the subshell ends. “

$ printf '%s\n' one two three | mapfile -t lines
$ printf '%d lines\n' "${#lines[@]}"
0 lines
$ mapfile -t lines < <(printf '%s\n' one two three)
$ printf '%d lines\n' "${#lines[@]}"
3 lines

Other examples

Compare command outputs

diff <(ls dir1) <(ls dir2)

Feed multiple streams into one command

comm <(sort file1) <(sort file2)

Logging while preserving output

make > >(tee build.log)

Hash data without temp files

md5sum <(curl -s example.com)