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 '...'

echo 'The $HOME is'

String with variables

Double "..."

echo "Home is $HOME"

Filename with spaces

Double "..."

grep "text" "$filename"

Glob patterns

No quotes

ls *.txt

Prevent globbing

Quotes

grep ".*" "$file"

Literal dollar sign

Escape \$

echo "Cost: \$5"

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

\$ or single quotes

`

Command substitution

\` or single quotes

*

Glob (any files)

\* or quotes

?

Glob (any char)

\? or quotes

[...]

Glob (char class)

\[ or quotes

;

Command separator

\; or quotes

&

Background job

\& or quotes

>

Redirect

\> or quotes

<

Redirect

\< or quotes

|

Pipe

| or quotes

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

$variable

$(command)

${param}

~ (home)

*.txt (glob)

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:

  1. Tokenization: Split the line into words (using spaces, special chars)

  2. Expansion: Replace $vars, $(commands), ~, *, etc.

  3. Quoting: Apply quote rules to prevent unwanted expansion

  4. 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.