100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Bash

Process Substitution

Learn how Bash's <(...) and >(...) process substitution let commands read from or write to another command's output as if it were a file.

Control FlowAdvanced9 min readJul 10, 2026
Analogies

What Process Substitution Actually Does

Process substitution, written <(command) for input or >(command) for output, is a Bash feature that runs command in the background and exposes its stdin or stdout as a filesystem path — typically /dev/fd/N on Linux — that another command can open just like a regular file. This matters because many programs, such as diff, only accept file paths as arguments and can't read from a pipe directly; <(command) lets you hand such a program the live output of a command without ever writing an intermediate temp file. Unlike a pipe, which connects exactly two commands' stdin/stdout, process substitution produces a path usable anywhere a filename is expected, including as multiple arguments to the same command.

🏏

Cricket analogy: Process substitution is like a broadcaster feeding a live match feed into a replay system as if it were a pre-recorded file, letting the third umpire 'open' the live stream the same way they'd open a saved clip.

Common Use Case: Diffing Command Output

The canonical use of process substitution is comparing the output of two commands without saving either to a temp file first: diff <(sort file1.txt) <(sort file2.txt) runs both sorts concurrently and hands diff two live file-like paths to compare. This pattern generalizes to any command that expects file arguments — comm, join, or even feeding <(command) as the input to a script that only knows how to read from a path — and it avoids the cleanup burden and race conditions that come with manually creating and deleting mktemp temp files.

🏏

Cricket analogy: diff <(sort teamA.txt) <(sort teamB.txt) is like comparing two teams' sorted batting averages side by side without printing either list to paper first — both sorted streams are compared live.

Output Substitution and Feeding Multiple Consumers

>(command) works in the opposite direction: it creates a path that, when written to, streams data into command's stdin, which is useful for splitting one stream of output to multiple destinations, as in some_process | tee >(gzip > out.gz) >(wc -l > count.txt) > raw.log, where tee fans the same output to a compressed file, a line-counting process, and a raw log simultaneously. Because each <(...) or >(...) spawns an actual subprocess connected via a named pipe or /dev/fd entry, exit status handling is subtler than with a normal pipeline — the main command's exit code doesn't reflect failures inside a process substitution, so critical error handling there needs its own explicit checks.

🏏

Cricket analogy: tee >(gzip > out.gz) >(wc -l > count.txt) fanning one stream to three destinations is like a stadium's video feed being split simultaneously to the big screen, the broadcast truck, and the archive server from one camera source.

bash
#!/usr/bin/env bash
set -uo pipefail

# Input process substitution: diff two live command outputs
diff <(sort users_prod.txt) <(sort users_staging.txt) > drift.diff || \
    echo "Environments have drifted; see drift.diff"

# Feed a while-read loop from a command via process substitution
# (avoids the subshell variable-loss problem of piping into while read)
total=0
while IFS= read -r size; do
    (( total += size ))
done < <(find . -maxdepth 1 -type f -printf '%s\n')
echo "Total bytes: $total"

# Output process substitution: fan one stream to three destinations
generate_report | tee \
    >(gzip > report.gz) \
    >(wc -l > report_lines.txt) \
    > report.log

Process substitution requires Bash (or another shell that supports it, like zsh or ksh) — it is not part of POSIX sh, so #!/bin/sh scripts running under dash will fail with a syntax error on <(...). Always use #!/usr/bin/env bash when a script relies on this feature.

  • <(command) exposes a command's stdout as a readable file path; >(command) exposes a writable path feeding a command's stdin.
  • It solves the problem of programs like diff that require file arguments but need to compare live command output.
  • Process substitution avoids manual mktemp temp files and their associated cleanup and race-condition risks.
  • tee >(cmd1) >(cmd2) > file fans one stream to multiple consumers simultaneously.
  • Feeding while read via < <(command) avoids the subshell variable-loss problem that piping into while read causes.
  • Exit status from inside a process substitution isn't automatically reflected in the main command's exit code.
  • Process substitution is a Bash extension, unavailable in POSIX /bin/sh — scripts using it need a #!/usr/bin/env bash shebang.

Practice what you learned

Was this page helpful?

Topics covered

#Bash#AdvancedBashScriptingStudyNotes#DevOps#ProcessSubstitution#Process#Substitution#Actually#Does#StudyNotes#SkillVeris