5.1. Syntax & Quoting#
Before you can work with variables, you need to understand how bash parses a command line. What looks simple—echo hello—involves several complex steps. Mastering quoting prevents 90% of beginner scripting errors.
5.1.1. Quick Reference#
Situation |
Use |
Example |
|---|---|---|
Literal string, no expansion |
Single |
|
String with variables |
Double |
|
Filename with spaces |
Double |
|
Glob patterns |
No quotes |
|
Prevent globbing |
Quotes |
|
Literal dollar sign |
Escape |
|
5.1.2. Debugging Quote Problems#
When bash gives confusing errors, you’re likely hitting quote issues:
$ echo $undefined_var something
something
# $undefined_var expanded to empty string
# Only "something" passed to echo
$ echo "$undefined_var" something
something
# Preserved empty string + space + "something"
Use set -x to see what bash actually does:
$ set -x
$ echo "Hello $name"
+ echo 'Hello Alice'
Hello Alice
# Shows the command after expansion
5.1.3. Quote Rules for Scripts#
Golden rule: Quote variables unless you specifically need globbing or word splitting.
# Variable in a command
$ grep "pattern" "$logfile" # ✓ RIGHT
$ grep pattern $logfile # ✗ WRONG (fragile)
# Variable in a path
$ rm "$backup_file" # ✓ RIGHT
$ rm $backup_file # ✗ WRONG (breaks with spaces)
# Variable in arithmetic (no quotes needed)
$ total=$((count + 1)) # ✓ RIGHT
$ total=$((count+1)) # ✓ Also works
5.1.4. Real-World Examples#
5.1.4.1. Processing Filenames with Spaces#
#!/bin/bash
# Search for a pattern in a file with spaces in name
search_term="error"
filename="My Important Log.txt"
# ✓ RIGHT: Quote everything
grep "$search_term" "$filename"
5.1.4.2. Building a Command Dynamically#
#!/bin/bash
# Build a complex grep command
pattern="ERROR|WARNING"
files="*.log"
# ❌ WRONG: Variables not expanded in single quotes
result=$(grep '$pattern' $files)
# ✓ RIGHT: Use double quotes for expansion
result=$(grep "$pattern" $files)
5.1.4.3. Preserving Literal Strings#
#!/bin/bash
# Use single quotes for regex patterns
regex='[0-9]\{3\}-[0-9]\{4\}' # Phone number pattern
# Use double quotes for variables
grep "$regex" "$datafile"
5.1.5. Combining Quotes#
You can combine quote types in one string:
# Mix single and double quotes
$ echo 'It'"'"'s nice'
It's nice
# 'It' + "'" + 's nice'
# Simpler approach:
$ echo "It's nice"
It's nice
# Single quote doesn't special in double quotes
# Also works:
$ name=Alice
$ echo 'Hello, '$name'!'
Hello, Alice!
# Single quote ends, variable, single quote resumes
5.1.6. Special Characters and Escaping#
Character |
Meaning |
How to Escape |
|---|---|---|
|
Variable expansion |
|
|
Command substitution |
|
|
Glob (any files) |
|
|
Glob (any char) |
|
|
Glob (char class) |
|
|
Command separator |
|
|
Background job |
|
|
Redirect |
|
|
Redirect |
|
|
Pipe |
|
5.1.7. Word Splitting#
Without quotes, bash splits on whitespace:
# One variable, treated as 3 arguments
$ words="one two three"
$ echo $words
one two three
# 3 arguments passed to echo
# With quotes, preserved as-is
$ echo "$words"
one two three
# 1 argument containing spaces
This matters in scripts:
#!/bin/bash
files="file1.txt file2.txt file3.txt"
# ❌ WRONG: Treated as multiple files
cat $files # 3 files passed to cat
# ✓ RIGHT in some contexts
cat $files
# But consider:
for f in $files; do
echo "File: $f"
done
# Expands to "one two three" per iteration
# ✓ BETTER: Use array or quote carefully
for f in "$files"; do
echo "File: $f"
done
# Might not split as expected either!
5.1.8. Common Quoting Scenarios#
5.1.8.1. Scenario 1: Variable with Spaces#
# ❌ WRONG: Without quotes, treated as multiple arguments
$ filename="My Document.txt"
$ cat $filename
cat: My: No such file or directory
cat: Document.txt: No such file or directory
# ✓ RIGHT: Quotes preserve it as one argument
$ cat "$filename"
# Opens "My Document.txt"
5.1.8.2. Scenario 2: Command Substitution#
# ❌ WRONG: Single quotes prevent execution
$ echo 'Current user is $(whoami)'
Current user is $(whoami)
# ✓ RIGHT: Double quotes allow execution
$ echo "Current user is $(whoami)"
Current user is alice
5.1.8.3. Scenario 3: Literal Dollar Sign#
# ❌ WRONG: Variable expanded
$ echo "Cost is $5"
Cost is 5
# bash thinks $5 is a variable!
# ✓ RIGHT: Escape the dollar sign
$ echo "Cost is \$5"
Cost is $5
5.1.8.4. Scenario 4: List of Files#
# ✓ Without quotes, glob expands to multiple arguments
$ ls *.txt
file1.txt file2.txt
# ✗ With double quotes, NOT expanded (one argument to ls)
$ ls "*.txt"
ls: *.txt: No such file or directory
# Single quotes also prevent globbing
$ ls '*.txt'
ls: *.txt: No such file or directory
5.1.9. Escaping: Override Quote Behavior#
Use backslash \ to escape the next character:
# Escape a dollar sign
$ echo "Cost is \$5"
Cost is $5
# Escape a quote
$ echo "He said \"hello\""
He said "hello"
# Escape special characters
$ echo "Path: /home/alice\* or /home/bob\*"
Path: /home/alice* or /home/bob*
# Prevents globbing
5.1.10. Expansion Types and Quote Behavior#
Expansion |
No Quote |
Single |
Double |
|---|---|---|---|
|
✓ |
✗ |
✓ |
|
✓ |
✗ |
✓ |
|
✓ |
✗ |
✓ |
|
✓ |
✗ |
✗ |
|
✓ |
✗ |
✗ |
Whitespace collapsing |
✓ |
✗ |
✗ |
5.1.11. The Three Quote Types#
Quoting controls how bash interprets special characters.
5.1.11.1. No Quotes (Bare Words)#
Without quotes, bash expands everything:
$ name=Alice
$ echo Hello $name
Hello Alice
# Expansion happens:
# - $name becomes Alice
# - Multiple spaces collapse to one
$ echo Hello $name
Hello Alice
# Globbing happens (wildcards expand)
$ echo *.txt
file1.txt file2.txt
# Commands are executed
$ echo $(date)
Thu Jan 15 10:30:45 PST 2025
# Tilde expands to home
$ echo ~
/Users/alice
5.1.11.2. Single Quotes: Literal Strings#
Single quotes prevent ALL expansion:
$ name=Alice
$ echo 'Hello $name'
Hello $name
# Variable NOT expanded
$ echo 'Today is $(date)'
Today is $(date)
# Command substitution NOT executed
$ echo '~'
~
# Tilde NOT expanded
Use single quotes when: You want a literal string with no expansion.
5.1.11.3. Double Quotes: Selective Expansion#
Double quotes allow SOME expansion but protect spaces and special chars:
$ name=Alice
$ echo "Hello $name"
Hello Alice
# Variable IS expanded
$ echo "Hello $name"
Hello Alice
# Spaces are PRESERVED (not collapsed)
$ greeting="Hello World"
$ echo $greeting
Hello World
# Without quotes, becomes 2 arguments!
$ echo "$greeting"
Hello World
# With quotes, preserved as 1 argument
# Command substitution still works
$ echo "Today is $(date)"
Today is Thu Jan 15 10:30:45 PST 2025
# But globbing does NOT work
$ echo "*.txt"
*.txt
# NOT expanded to list of files
Use double quotes when: You need expansion but want to preserve whitespace and prevent globbing.
5.1.12. How Bash Parses a Command#
When you type a command, bash goes through these steps:
Tokenization: Split the line into words (using spaces, special chars)
Expansion: Replace
$vars,$(commands),~,*, etc.Quoting: Apply quote rules to prevent unwanted expansion
Execution: Run the command with the resulting arguments
Example:
$ echo Hello World
Bash sees:
Token 1:
echo(the command)Token 2:
Hello(first argument)Token 3:
World(second argument)
Multiple spaces are treated as a single separator. The command receives 2 arguments.