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)