Skip to content
daniel@MacBookPro:~
Shell & TerminalDeep Dive September 2, 2026 3 min read

Shell Scripting Pitfalls: Quoting, Word Splitting, and Why $var Isn't Always Safe

An unquoted variable works fine in testing, then silently breaks in production the first time it holds a value with a space in it. This is the single most common category of real-world shell scripting bug.

An unquoted variable reference is arguably the single most common source of real, production-breaking shell scripting bugs — it works fine through casual testing, then fails the first time the variable’s actual value contains something the script’s author didn’t anticipate.

The core problem: word splitting

file="my document.txt"
rm $file
# attempts to remove two separate files: "my" and "document.txt"

Without quotes, $file’s expanded value is subject to word splitting — the shell breaks it into separate words on whitespace before passing it as arguments, meaning a single variable holding a filename with a space becomes two separate arguments instead of one.

The direct fix: quote every variable expansion

rm "$file"
# correctly treats the entire value as one argument

Double-quoting a variable reference disables word splitting (and glob expansion) for that expansion specifically, while still allowing variable and command substitution inside the quotes — this single habit prevents the large majority of variable-related shell scripting bugs.

Why this specific bug is so easy to miss in testing

Test values chosen during development (test.txt, myfile) rarely contain spaces, special characters, or glob metacharacters — the bug only surfaces once the script runs against real-world data (a user’s actual filename, an argument passed from another program) that happens to contain a space or wildcard character the developer’s test cases never included.

Single quotes vs. double quotes: a genuinely different behavior

name="World"
echo "Hello, $name"    # Hello, World  (double quotes: expansion happens)
echo 'Hello, $name'    # Hello, $name  (single quotes: no expansion at all)

Double quotes still allow variable and command substitution inside them; single quotes suppress all expansion, treating everything inside literally — using the wrong one is a common, specific mistake distinct from the “forgot to quote at all” problem.

Why [ -z $var ] is a specific, well-known trap

[ -z "$var" ]     # correct: safe even if $var is unset or contains spaces
[ -z $var ]       # broken if $var is unset — becomes [ -z ] , a syntax error

If $var is unset or empty, the unquoted version can collapse into invalid syntax for the test command entirely — a bug that only manifests for specific, easy-to-forget-to-test input states (empty or unset variables), rather than failing consistently regardless of input.

What shellcheck actually catches, and why it’s worth running

ShellCheck is a static analysis tool specifically built to catch this entire category of shell scripting pitfalls — unquoted expansions, common globbing mistakes, and dozens of other well-known failure patterns — before a script ever actually runs against real data where the bug would otherwise surface.

Why “it worked when I tested it” isn’t the same as “it’s correct”

Shell scripting bugs in this category are specifically dangerous because they’re input-dependent — a script can run correctly across dozens of successful executions and then fail (or, worse, silently do the wrong thing, like deleting the wrong file) the first time it encounters an input value with a space, a leading dash, or a glob character the developer’s testing never happened to include.

The practical habit that prevents almost all of these

Quoting every variable expansion by default — not just the ones that “seem like” they might contain spaces — is the single highest-value habit in shell scripting specifically because you generally can’t be certain in advance which variable will eventually hold an unexpected value; consistent quoting removes the need to guess correctly every time.