Troubleshooting
Scope: the errors and gotchas you'll actually hit, what they mean, and the plain fix for each. Verified against Bash 5.2.
Errors
"command not found: name"
You typed name = "Ada" with spaces — Bash read name as a
command. Fix: remove the spaces: name="Ada".
"No such file or directory" on a script that exists
Two usual causes:
- Missing shebang or
chmod +xwhen running./script.sh. - Windows line endings (
\r\n) — the\rbecomes part of the interpreter path or arguments. Fix:dos2unix script.shorsed -i 's/\r$//' script.sh.
"unbound variable"
set -u caught you using a variable that was never set — often
a typo. Fix: check the spelling; use ${var:-default} if
empty is legitimate.
"syntax error near unexpected token"
Usually a paren/brace/quote mismatch, or Windows line endings.
Fix: run with bash -n script.sh (syntax check only) to find
the line, then eyeball the quoting.
"ambiguous redirect"
echo hi > $outfile # fails if $outfile is empty or has spaces
Fix: quote it: > "$outfile", and make sure it's set
(set -u catches the empty case).
Gotchas (runs fine, surprises you)
Unquoted variables word-split
file="my notes.txt"
rm $file # tries to remove "my" and "notes.txt"!
Fix: "$file", always. (See
Variables and Quoting.)
for line in $(cat file)
Word-splits and glob-expands the file contents. Fix: the
while IFS= read -r line loop.
Glob didn't match anything
for f in *.txt with no .txt files iterates once with the
literal string *.txt. Fix: shopt -s nullglob.
cd failed, rest of script ran elsewhere
cd "$dir"
rm -rf ./* # if cd failed, this runs in the WRONG directory
Fix: cd "$dir" || exit 1 — always guard cd.
Variables set inside a pipeline disappear
cat f.txt | while read -r l; do count=$((count+1)); done
echo "$count" # empty — the loop ran in a subshell
Each pipeline stage runs in a subshell; its variables die with
it. Fixes: redirect instead of piping (while read... done
< f.txt), or shopt -s lastpipe (Bash 4.2+, non-interactive
only).
set -e didn't stop on failure
It's ignored in if conditions, &&/|| lists, and command
substitutions in some positions. If you need guaranteed failure,
be explicit: cmd || die "cmd failed".
Debugging technique
bash -n script.sh— syntax check, runs nothing.bash -x script.sh— trace every command as it runs.- Narrow it:
set -x/set +xaround the suspicious section. shellcheck script.sh— a linter that catches most of the above automatically. Install it; run it on every script.
ShellCheck deserves emphasis: it's like a compiler warning
system for Bash, and it will teach you the language faster than
any guide. apt install shellcheck / brew install shellcheck.
Check
- [ ] You check
bash -nthenbash -xwhen debugging - [ ] You've installed ShellCheck and run it on a script
- [ ] You guard every
cdwith|| exit 1
Next: Glossary.