Use process substitution
Treat a command's output as a file argument to another command — e.g. `diff <(sort a) <(sort b)` — without manual tempfile management.
How to use process substitution in each shell
diff <(sort file_a) <(sort file_b)`<(cmd)` runs `cmd` in a subshell, exposes its stdout via `/dev/fd/N` (or `/proc/self/fd/N` on Linux), and passes that pseudo-file path as the argument. Works for any tool that takes a filename and reads sequentially.
diff =(sort file_a) =(sort file_b)diff (sort file_a | psub) (sort file_b | psub)Fish has NO `<(...)` syntax. `psub` ("process substitution") is a builtin that captures stdin to a temp file and prints the path — caller passes the printed path as a file argument. `psub` defaults to a regular file; for FIFO behavior use `psub --fifo`.
diff (sort-object -inputobject (Get-Content file_a)) (sort-object -inputobject (Get-Content file_b))pwsh has NO process-substitution syntax. Workarounds: (1) write outputs to temp files first: `sort file_a > a.tmp; sort file_b > b.tmp; diff a.tmp b.tmp`; (2) compare objects directly: `Compare-Object (Get-Content file_a | Sort-Object) (Get-Content file_b | Sort-Object)`.
sort file_a > a.tmp & sort file_b > b.tmp & fc a.tmp b.tmpcmd has NO process substitution. The temp-file workaround above is the only path. `&` chains commands sequentially regardless of exit code; `&&` chains only on success.
Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.
Gotchas & notes
- **Why process substitution exists**: many Unix tools (diff, paste, comm, join, vimdiff) take FILENAMES, not stdin. Without process substitution you'd need temp files: `sort a > /tmp/a.sorted; sort b > /tmp/b.sorted; diff /tmp/a.sorted /tmp/b.sorted; rm /tmp/{a,b}.sorted`. With `<(...)`: `diff <(sort a) <(sort b)` — one line, no tempfile cleanup, the kernel manages the /dev/fd/N pseudo-file lifecycle. The trade-off: many tools (gzip with `-l`, ffmpeg with seeking) expect SEEKABLE files; `/dev/fd/N` is a pipe (non-seekable) — those tools fail.
- **Direction**: `<(cmd)` is input substitution (cmd's stdout → fake file for the outer command to READ). The lesser-used `>(cmd)` is output substitution (outer command WRITES to a fake file, the bytes appear on `cmd`'s stdin): `gzip > >(split -b 100M - chunk_)` writes gzip output through `split` without an intermediate file. Most use cases are read; the write form is niche.
- **zsh-specific `=(cmd)`** is a THIRD form unique to zsh — it captures `cmd`'s output to a REAL temp file (not a /dev/fd pipe). Useful when the consumer NEEDS a seekable file (e.g. `mpv =(curl -s url)` — mpv must seek the video). Bash has no built-in equivalent; you write `f=$(mktemp); curl -s url > "$f"; mpv "$f"; rm "$f"`. zsh `=(...)` cleans up the temp file automatically when the outer command exits.
- **Fish `psub` caveats**: `psub` defaults to writing to a regular TEMP FILE (auto-deleted after the outer command exits). For larger streams it can be slow because the entire stdin is buffered before the path is printed. Use `psub --fifo` for true FIFO behavior — the path points at a named pipe, no buffering. But FIFOs only support ONE reader-pass: `diff (... | psub --fifo) (... | psub --fifo)` works (diff reads each once); `paste -d "," (... | psub --fifo) (... | psub --fifo)` ALSO works; but `cat (... | psub --fifo)` followed by re-reading would fail. pwsh and cmd have no equivalent at all — the temp-file workaround is the only option, with manual cleanup needed for non-`Remove-Item` paths.
Related commands
Related tasks
- Append command output to a file— Add a command's output to the END of a file (creating it if missing) without erasing existing content — for incremental logs, config patches, and accumulators.
- Merge stderr into stdout— Combine a command's error stream with its normal output so a single pipe / file / variable captures BOTH — essential for log aggregation and CI capture.
- Tee command output to a file and stdout— Write a command's stdout to a file AND echo it to the terminal in one pass — for logged-but-watchable installer / build / CI runs.