Bash Process Substitution
Use process substitution to pass command output as file arguments, enabling powerful shell pipelines.
Process Substitution Basics
# <(cmd) — treats command output as a file (read)
diff <(sort file1.txt) <(sort file2.txt)
comm <(sort a.txt) <(sort b.txt)
wc -l <(find . -name "*.go")
# >(cmd) — treats command input as a file (write)
tee >(gzip > out.gz) >(wc -l) > /dev/null
# Real use: compare remote and local files
diff <(ssh host cat /etc/hosts) /etc/hosts
# Multiple substitutions
join <(sort file1) <(sort file2)
Here-String & Here-Doc
# Here-string: <<< "string"
grep 'pattern' <<< "search in this string"
base64 -d <<< "aGVsbG8="
read -r a b c <<< "one two three"
# Here-doc: << DELIM
cat << EOF
Line 1
Line 2: $variable (expanded)
EOF
# Indented here-doc (<<-)
if true; then
cat <<- EOF
Indented content (leading tabs stripped)
EOF
fi
# Quoted delimiter — no expansion
cat << 'EOF'
$NOT_EXPANDED \n literal
EOF
Named Pipes (FIFOs)
# Create named pipe
mkfifo /tmp/mypipe
# Terminal 1: writer
echo "hello" > /tmp/mypipe
# Terminal 2: reader
cat < /tmp/mypipe
# Persistent logging pipeline
mkfifo /tmp/log_pipe
tee /tmp/log_pipe | logger -t myapp &
your_program > /tmp/log_pipe
# Cleanup
rm /tmp/mypipe
Command Grouping
# ( ) — subshell grouping (changes don't affect parent)
(cd /tmp && ls) # pwd unchanged after
(export VAR=val; ./script) # VAR not set in parent
# { } — current shell grouping
{ echo "a"; echo "b"; } | grep a
{ cd /tmp && ls; } # cd affects current shell (note: space and ; required)
# Redirect group output
{ cmd1; cmd2; cmd3; } > output.txt 2>&1
# Pipe group into while
{ echo "line1"; echo "line2"; } | while read line; do
echo "Got: $line"
done
Practical Pipelines
# Log to multiple destinations simultaneously
./app 2>&1 | tee >(grep ERROR > errors.log) >(grep WARN > warns.log) | cat
# Parallel processing with process substitution
paste <(cut -f1 file) <(cut -f2 file | tr 'a-z' 'A-Z')
# Check if two commands produce same output
if diff <(cmd1) <(cmd2) > /dev/null; then
echo "Same output"
fi
# Capture stderr separately
exec 3>&1
stderr=$(./script 2>&1 1>&3)
exec 3>&-
# Pipeline with error handling
set -o pipefail # return non-zero if any pipe cmd fails
cmd1 | cmd2 | cmd3