Linux Basic — Avoid Unnecessary Forking
Avoid Unnecessary Forking
Every subshell, backtick, and external command is a fork() + exec(). Inside a loop of 10,000 iterations, that’s 10,000 forks — and it’s slow.
Subshells: ( ... )
A subshell runs in a child process. Useful for isolation, expensive when overused.
# SLOW — 10k subshells
for i in {1..10000}; do
(cd /tmp && do_something)
done
# FAST — one cd, manual restore
orig=$PWD
cd /tmp || exit
for i in {1..10000}; do
do_something
done
cd "$orig" || exit
Or use pushd/popd:
pushd /tmp >/dev/null
for i in {1..10000}; do
do_something
done
popd >/dev/null
Backticks vs $(...)
Both fork, but $(...) is clearer and nestable:
# old style, hard to nest
result=`command`
# modern, nest freely
result=$(outer $(inner))
Both have the same cost — one fork per substitution.
Pipes Create Subshells
The right-hand side of a pipe runs in a subshell:
# SLOW — cat is an unnecessary fork
cat file | while read line; do
process "$line"
done
# FAST — redirect from file
while read line; do
process "$line"
done < file
Every cat file | ... can be ... < file.
echo Piped to a Command
# SLOW — fork for echo
echo "data" | process
# FAST — here-string (no fork)
process <<< "data"
Test for Command Existence
# SLOW — forks which/command
if which grep &>/dev/null; then
# FAST — builtin
if command -v grep &>/dev/null; then
command -v is a Bash builtin — no fork.
Quick Wins
| Replace | With | Why |
|---|---|---|
cat file \| cmd |
cmd < file |
no cat fork |
echo "$x" \| cmd |
cmd <<< "$x" |
no echo fork |
$(cd /tmp && cmd) |
manual cd + restore | no subshell |
| backticks | $(...) |
readability (same cost) |
Next → Builtins vs External