Best Practices For Quoting And Bracing Shell Variables
Why Proper Variable Quoting and Bracing Matters
Quoting and bracing shell variables properly is critical for writing robust bash scripts. Unquoted variables can enable unintended command execution and injection attacks. They can also cause scripts to fail due to undefined variables or whitespace issues.
Always quoting variables preserves the integrity of their values and prevents whitespace from being interpreted as command separators. Braces enable precise variable expansion and prevent glob patterns from splitting variable values in unexpected ways.
Proper quoting and bracing of shell variables in bash scripts has several key benefits:
- Prevents unintended code execution from unquoted variables
- Eliminates errors from undefined or null variables
- Allows safe use of spaces, wildcards and special symbols in variables
- Enables precise control over variable expansion with braces
- Strengthens script security against injection attacks
- Makes scripts more robust and reliable across systems
When to Quote Variables
Knowing when to quote variables is just as important as how to quote them properly. Here are common cases where quoting variables is essential:
Around Variable References to Preserve Whitespace
Double quoting variables preserves all characters inside them, including whitespace like spaces, tabs and newlines. Without quotes, whitespace breaks variables into multiple tokens:
varname="some string" echo $varname => some string echo "$varname" => some string
Failing to quote variables containing whitespace can lead to errors or unintended command execution:
varname="ls -l" echo $varname => ls -l gets executed echo "$varname" => ls -l printed as string
When Expanding Variables with Undefined Values
Unquoted variables with undefined values expand to empty strings that can break commands or enable unintended code execution:
undefinedvar= echo "Hello $undefinedvar" => Hello echo Hello $undefinedvar => Hello ls -l /home /*executes*/
Always quote undefined variables to substitute them with empty values instead of breaking script logic.
When Variables Reference Filenames with Spaces
Spaces in filenames can lead to errors if unquoted in variable references:
filename="my file.txt" cat $filename => Error cat "$filename" => Works
Quote variables containing filenames to preserve space integrity.
Brace Expansion Rules and Guidelines
Braces {} around variable names control their expansion behavior. Brace expansion has some notable rules and guidelines:
Prevent Split Words and Incorrect Glob Patterns
Unbraced variables containing multiple words or wildcards get split incorrectly:
varname="my important file.txt" echo $varname => my important file.txt rm $varname => Errors rm "$varname" => Works varname2="*.txt" rm $varname2 => Removes all .txt files rm {$varname2} => Works as intended
Add braces around references to preserve all characters during expansion.
Escape Braces When Used Literally Instead of For Expansion
To include literal braces {} in variable values, escape them by adding \{ or \}:
varname="{content}" echo $varname => Errors echo "${varname}" => {content} echo "${varname//\{/\}}" => {}}content{
Watch Out For Command Injection Vulnerabilities
Unbraced variables allow injection attacks if unsafely embedded in larger commands:
varname="--option" mycommand $varname => Injection risk mycommand {$varname} => Safe expansion
Always brace variables embedded in larger commands to avoid vulnerabilities.
Quoting Styles for Different Effects
Bash has various quoting mechanisms, each with different effects on variable expansion and execution:
Double Quotes Allow Variable and Command Substitution
Double quotes ” expand nested variable references and $(commands):
varname="name.txt" echo "Copying $varname to $(pwd)" => Copying name.txt to /home/user
Single Quotes Treat Everything Literally
Single quotes ‘ disable all expansions, treating every character inside them literally:
varname="name.txt" echo 'Copying $varname to $(pwd)' => Copying $varname to $(pwd)
Use single quotes to suppress expansions when needed.
Escape Characters For Inserting Quotes and Special Symbols
The backslash \ character escapes quotes and other special symbols:
varname="He said \"hello\" today" echo "$varname" => He said "hello" today
Use escape sequences like \”, \$, \\ to insert quotes and other symbols into quoted strings.
Examples of Problematic Unquoted Variables
Failing to quote variables properly can lead to all kinds of unexpected errors and behaviors:
Whitespace Handling Issues
# Spaces split words varname="file name" ls $varname # Errors # Tabs and newlines break things varname="1st line 2nd line" echo $varname # Errors
Glob Pattern and Wildcard Expansions
varname="*.txt" rm $varname # Removes all .txt files dangerously varname="my???file" ls $varname # Unintended wildcard matching
Split Variable Values
# Unbraced variables split on spaces varname="my file.txt" echo $varname # => my file.txt rm $varname # Removes "my" file # Brace variables to avoid splitting echo ${varname} # => my file.txt rm {$varname} # Works safely
Injection Attacks
varname="--verbose" program $varname # Enables verbose mode unexpectedly varname="1); echo pwned # " function "$varname" # Command injection attack
These examples demonstrate the critical importance of proper quoting and bracing of variables in shell scripts.
Proper Formatting with Quotes and Braces
Here are some standard guidelines and best practices for correct variable quoting and bracing in bash:
Always Quote Variable References
"Use double quotes around variable names" 'Single quotes also work in many cases'
Quoting variables preserves whitespace and special characters.
Brace Unquoted Variable References
echo ${varname} # Braces even without quotes [[ -e ${filename} ]] # Useful in conditionals
Braces enable safe expansion and prevent word splitting issues.
Escape Nested Quotes and Symbols
varname="He said \"hello\"" # Escape inner quotes echo ${varname//search/replace} # Escape slashes
Use the backslash to sanitize nested quotes, braces and other special symbols.
Break Up Complex Commands
files=$(ls) file_count=${#files[@]} # Split complex commands echo "Found $file_count files"
Split complex variable expansions across multiple lines for readability.
Compare Good and Bad Examples
# Bad printf "Files in $dir: " ls $dir # Good dir="/some/path" printf "Files in %s: " "$dir" ls "{$dir}"
Contrast poorly quoted code with well-quoted, braced examples side-by-side.
These tips will help you quote, brace and format shell variables safely, robustly and idiomatically.