Reading — step 1 of 4
Learn
~1 min readPerformance and Tools
Bash is not fast. Even simple loops with subshells can take seconds where C would take milliseconds. Knowing what's slow saves hours.
The big rule: avoid forking. Every pipe stage, every $(), every external command is a fork.
Slow patterns:
# BAD — forks for every iteration
for i in $(seq 1 1000); do
count=$(echo "$i" | wc -c)
done
# GOOD — pure bash
for (( i = 1; i <= 1000; i++ )); do
count=${#i}
done
Replacements for common slow patterns:
# String length
${#var} # not: echo "$var" | wc -c
# Substring
${var:5:10} # not: echo "$var" | cut -c 6-15
# Replace
${var//foo/bar} # not: echo "$var" | sed 's/foo/bar/g'
# Split on delimiter
IFS=':' read -ra parts <<< "$path" # not: cut -d: -f1 ...
# Test condition
[[ -f "$f" ]] # not: test -f "$f"
[[ "$x" =~ ^[0-9]+$ ]] # not: echo "$x" | grep -E ...
Profiling:
# Whole-script wall time:
time ./script.sh
# Per-line trace with timestamps:
PS4='+ $EPOCHREALTIME\011 '
set -x
# ... your script ...
$EPOCHREALTIME (Bash 5+) is microsecond-precision. The trace shows where time goes.
Built-in times — print user/sys CPU since shell start:
times
# 0m0.012s 0m0.008s
# 0m0.000s 0m0.000s
Read input efficiently:
# Slow — fork per line
while IFS= read -r line; do
echo "$line"
done < big_file
# Faster — read whole file into memory
mapfile -t lines < big_file
for line in "${lines[@]}"; do
echo "$line"
done
Async — background processes:
results=()
for host in "${hosts[@]}"; do
{
ping -c1 "$host" > /tmp/p_$$_${host} &
}
done
wait # block until all finish
The & runs in background. wait (no args) waits for ALL children.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…