Meet the quoting and subshell traps head on

short · 30 min · Objective 4.2

Task

Reproduce the four bash traps that produce silent wrong answers rather than errors: the subshell that discards a loop counter, the unquoted variable, the [ that creates a file, and the destructive expansion of an unset variable. Seeing each fail is worth more than reading the rule.

Steps

  1. The subshell counter: run n=0; printf 'a\nb\nc\n' | while read x; do n=$((n+1)); done; echo $n and observe it prints 0. Then fix it with a redirect (done < <(printf ...)) and observe 3. Explain the subshell.
  2. The unquoted variable: create a file named two words.txt, then f="two words.txt"; ls $f fails looking for two files, while ls "$f" works.
  3. The [ file trap: run [ 5 > 3 ] and then ls -- confirm a file named 3 now exists, and explain that > was redirection. Show [ 5 -gt 3 ] is the correct numeric test.
  4. The destructive expansion: create a subdirectory with a file, then demonstrate SAFELY why rm -rf "$dir"/ is dangerous with dir unset -- use echo rm -rf "${dir}/" (with echo, so nothing is deleted) and see it expand to rm -rf /. Then show ${dir:?} refusing.
  5. Add set -euo pipefail to a small script and show it catching an unset variable and a failing pipeline stage that the default would have ignored.
  6. Show $(...) nesting where backticks would need escaping.

Verify

cd ~/lab-bash
n=0; while read x; do n=$((n+1)); done < <(printf 'a\nb\nc\n'); test "$n" = 3 && echo "redirect keeps the counter"
test -f 3 && echo "[ 5 > 3 ] created a file called 3" && rm -f 3
unset dir; ( set -u; echo "safe: ${dir:?must be set}" ) 2>&1 | grep -qi 'must be set' && echo "guard refused empty var"

The counter reaching 3 only with the redirect, and the file named 3 existing after [ 5 > 3 ], are the two that stick. Both are silent in normal use: no error, just a wrong answer or a stray file.

Notes

Every trap here fails quietly. That is the whole reason to meet them deliberately: a syntax error announces itself, but a subshell eating your counter or a string comparison masquerading as numeric just gives you the wrong number, and you find out much later.