Definition
Word splitting is the behavior where a shell (a command interpreter such as bash or zsh), after expanding a variable or command substitution, breaks the resulting string into multiple arguments on whitespace — spaces, tabs, and newlines. Quoting wraps the value in double quotes "..." to prevent this splitting, so the whole value is passed as a single argument.
In short: even when the value stored in a variable is correct, if you don't quote it the shell chops that value into pieces before handing it to the command.
Why it matters
Consider a path that contains a space. If you write DIR="/tmp/My Files" and then ls $DIR, the shell expands $DIR and splits it on the space, passing ls, /tmp/My, and Files. ls receives two nonexistent paths and fails with "no such file." The value itself (echo "$DIR") is clearly correct, yet only the command fails — a symptom that trips up beginners for a long time.
In places people rarely look at, such as hook scripts or CI configuration, this mistake breaks silently. Because values that may contain spaces or special characters — home directory paths, plugin roots, user-created filenames — are genuinely common, you will keep getting caught by this if you don't know the rule.
How it works
To run a single line, the shell roughly goes through these steps:
- Expansion: substitutes
$VAR,${VAR},$(cmd), and so on with their actual values. - Word splitting: divides the expansion result into multiple tokens using the characters in
IFS(Internal Field Separator, which defaults to space, tab, and newline). This step applies only to unquoted expansions. - Pathname expansion (globbing): expands patterns like
*and?into filenames (again only when unquoted). - Passes the final tokens as the command's arguments.
Wrapping with "$VAR" skips steps 2 and 3 entirely, so the value is preserved as a single argument. Single quotes '...' block step 1 (expansion) itself and treat the content as a literal.
In practice
The static checker shellcheck automatically catches missing quotes (SC2086 and others). Running it once before you put a script into CI is the cheapest defense there is.
Trade-offs
- Quoting has almost no cost. You can make "always quote" your default and lose essentially nothing — the exceptions are only the few cases where splitting is intentional.
- When you do need intentional splitting (for example, when you want to expand a space-separated option list into arguments), you either omit the quotes or use an array. Even then, an array is safer and clearer.
When not to use
- If you cannot be certain that a value will never contain spaces or special characters, do not drop the quotes. "It's fine for now" breaks on a future filename or path.
- Dropping quotes to rely on word splitting for "expanding into multiple arguments" is an anti-pattern in shells that have arrays —
"${arr[@]}"is safer.
Common mistakes
- Passing
$VARto a command without quotes. If the value is correct but the command fails, this is the first place to suspect. - Failing to handle empty values:
cmd $EMPTYbecomes zero arguments, so later arguments shift forward."$EMPTY"keeps a single empty string. - Not quoting command substitution:
x=$(cmd)collapses newlines into spaces and splits. Wrap it as"$(cmd)". - Expecting variable expansion inside single quotes:
'$VAR'is the literal$VAR. Use double quotes to interpolate a value.
Related concepts
- serverless-stateless-execution — quoting mistakes tend to surface in contexts where paths and state differ from one execution environment to the next