Preventing Word Splitting and Globbing in Bash: A Variable Expansion Guide
Learn how to prevent word splitting and globbing in Bash using proper quoting and expansion strategies to ensure script stability when handling spaces and wildcards.
20 Apr 2026, 08:10 UTC

The Problem: Unpredictable Argument Parsing
When you reference a variable in Bash without quotes, the shell does not treat the resulting value as a single string. Instead, it performs Word Splitting (breaking the string into multiple arguments based on the Internal Field Separator, or IFS) and Pathname Expansion (treating characters like * or ? as wildcards to find matching files). This often leads to scripts that fail when filenames contain spaces or when user input contains special characters.
Expansion Strategy Comparison
Choosing the right expansion method depends on whether you need to preserve the literal value of the variable or manipulate it before passing it to a command.
| Syntax | Word Splitting? | Globbing? | Best Use Case |
|---|---|---|---|
$VAR |
Yes | Yes | Rarely recommended; only for intentionally split lists. |
\"$VAR\" |
No | No | Standard variable passing; preserves spaces and wildcards. |
${VAR} |
Yes | Yes | Variable interpolation within longer strings (e.g., ${VAR}_file). |
\"${VAR}\" |
No | No | Complex expansions (substrings, defaults) that must remain one argument. |
\"${ARRAY[@]}\" |
No | No | Iterating over array elements while preserving spaces in each element. |
Engineering Trade-offs
While double-quoting is the safest default, different constraints require different approaches:
- Strictness vs. Flexibility: Using
set -u(nounset) prevents the script from continuing if a variable is undefined. This is critical for production scripts where an empty variable passed torm -rf \"$DIR/\"could accidentally target the root directory if$DIRis unset. - Scope Management: Using the
localkeyword inside functions ensures that variable expansions do not accidentally overwrite global state, which is a common source of bugs in larger Bash frameworks. - Array Handling: Using
${ARRAY[*]}(with an asterisk) expands all elements into a single string, whereas${ARRAY[@]}(with an @ symbol) treats each element as a distinct quoted string.
Implementation and Validation
The following example demonstrates the difference between unquoted and quoted expansions when dealing with "dirty" data (strings containing spaces and wildcards).
# Run this in a Bash terminal (Version 4.0+)
# Setup: Create a file with a space in the name
touch \"my test file.txt\"
# Scenario 1: The failure of unquoted variables
FILE_NAME=\"my test file.txt\"
ls $FILE_NAME
# Expected result: ls: cannot access 'my': No such file or directory
# (and similar errors for 'test' and 'file.txt')
# Scenario 2: The success of quoted variables
ls \"$FILE_NAME\"
# Expected result: my test file.txt
# Scenario 3: Preventing globbing
WILDCARD=\"*\"
echo $WILDCARD
# Expected result: Lists all files in the current directory
echo \"$WILDCARD\"
# Expected result: *
Diagnostic Checklist for Production Scripts
To verify your script is resilient to word splitting and globbing, check for these three patterns:
- The Quote Check: Search for any
$VARthat is not wrapped in double quotes. If it is being passed as an argument to a command, it should likely be\"$VAR\". - The Undefined Check: Ensure
set -uis declared at the top of the script. Test this by referencing a variable that hasn't been initialized; the script should terminate immediately with anunbound variableerror. - The Array Check: Ensure arrays are expanded as
\"${ARRAY[@]}\". To verify, create an array with an element containing a space:ARR=(\"item one\" \"item two\"). Loop through it usingfor i in \"${ARR[@]}\"; do echo \"$i\"; done. If it prints four lines instead of two, your quoting is incorrect.
Rollback and Recovery
If you have already deployed a script and find that set -u is causing too many crashes due to optional variables, you can provide a default value during expansion instead of removing the flag:
# Instead of removing 'set -u', use the default value syntax:
# ${VAR:-default_value}
echo \"${OPTIONAL_VAR:-guest}\"
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.