Subshells, Grouping, and Process Substitution
Prerequisites: Pipes: connecting commands, Functions
Subshells: ( )
Commands in parentheses run in a subshell — a copy of the shell.
Changes inside (variables, cd) don't affect the parent:
mkdir -p build
count=1
(
cd build
count=99
echo "inside: $(basename "$PWD"), count=$count"
)
echo "outside: count=$count"
inside: build, count=99
outside: count=1
Subshells are handy for doing something in another folder without having
to cd back. Pipelines and $(...) also run in subshells — which is why a
variable changed in cmd | while read ... is lost afterwards.
Grouping: { }
Braces group commands in the current shell — useful for redirecting several commands' output at once:
{
echo "Report"
echo "======"
date -d @0 -u +%F
} > report.txt
cat report.txt
Report
======
1970-01-01
Note the syntax: a space after { and a newline (or ;) before }.
Process substitution: <( )
<(command) makes a command's output look like a file, so you can pass
it to programs that expect file names:
printf '%s\n' apple banana cherry > list1.txt
printf '%s\n' banana cherry date > list2.txt
diff <(sort list1.txt) <(sort list2.txt)
echo "---"
comm -12 <(sort list1.txt) <(sort list2.txt)
1d0
< apple
3a3
> date
---
banana
cherry
diffcompares two sorted outputs directly — no temporary files.comm -12prints lines common to both.
Keeping variables when reading command output
Process substitution also fixes the lost-variables problem:
total=0
while read -r n; do
(( total += n ))
done < <(printf '%s\n' 5 10 15)
echo "total=$total"
total=30
done < <(command) feeds the command's output into the loop without a
pipe, so the loop runs in the current shell and total survives.
Common mistakes
- Writing
< <(...)without the space between the two<characters. - Expecting
cdinside$(...)or a pipeline to change the script's folder.