Quoting & Escaping
Why Quoting Matters
When you type a command, Bash performs word splitting: it breaks unquoted text at characters in IFS (spaces, tabs, newlines by default). Quoting suppresses that splitting and the interpretation of special characters. Nearly every "my script only breaks on filenames with spaces" bug is a quoting bug.
Word Splitting Demonstrated
file="my report.txt"
rm $file # WRONG: becomes "rm my report.txt" -> deletes my and report.txt
rm "$file" # RIGHT: one argument, spaces preserved
The shell cannot see spaces the way you do. To Bash, a space is a separator unless you quote it.
The Three Quote Forms
Double Quotes " "
Double quotes preserve spaces but still allow parameter expansion ($var), command substitution ($(...)) and backslash escapes for a few characters. This is the default choice.
name="world"
echo "Hello, $name" # Hello, world (variable expanded)
echo "Today is $(date +%F)" # command substitution expanded
echo "cost: \$5" # \$ protects the dollar sign: cost: $5
Single Quotes ' '
Single quotes suppress everything. Not a single character is special, including the backslash. Use them for literal text such as regular expressions, awk programs or passwords.
echo 'Hello, $name' # Hello, $name (no expansion)
grep -E '^[0-9]{3}-' phones # regex kept exactly as written
You cannot put a single quote inside single quotes directly. End the string, escape the quote, restart:
echo 'it'\''s here' # it's here
Backslash \
Outside quotes, a backslash removes the special meaning of the next character, and a backslash-newline continues the line.
echo \$HOME # $HOME (literal, not the variable)
echo \$HOME is $HOME # $HOME is /home/me
cp file\ with\ space dest # one filename containing a space
$'...' ANSI-C Quoting
Bash also understands $'...', which interprets C-style escapes. This is the clean way to emit control characters such as tab and newline.
printf '%s' $'line1\nline2\n' # \n becomes a real newline
echo $'a\tb' # ab
echo $'\u00e9' # é — a Unicode code point
Common Escape Sequences
| Escape | Meaning | Example |
|---|---|---|
\n | Newline | $'a\nb' |
\t | Tab | $'a\tb' |
\\ | Literal backslash | $'a\\b' |
\' | Literal single quote | $'it\'s' |
\xNN | Hex byte | $'\x41' is A |
Here-Documents & Here-Strings
Here-Documents
A here-document feeds a block of text to a command's standard input until a delimiter line. It removes the need to escape quotes inside the block.
cat <<'EOF'
This text is literal: $PATH, $(id) and "quotes" are untouched.
EOF
cat <<EOF
This one expands: your PATH is $PATH.
EOF
Quoted vs Unquoted Delimiter
The rule is simple: quote the delimiter (<<'EOF') to keep the body literal; leave it unquoted to allow expansion. Use <<-EOF to strip leading tabs.
sudo tee /etc/motd <<-'EOF'
Welcome to the server
Report problems to ops@example.com
EOF
Here-Strings
<<< feeds a single string plus a newline. It is a tidy replacement for echo "$x" | cmd.
grep -c "error" <<<"$log_line"
read -r first rest <<<"one two three"
Quoting Pitfalls
Unquoted Globs
An unquoted * expands to filenames. If you meant a literal asterisk, quote it: grep 'a*b' file, not grep a*b file.
Empty Variables
"$var" with an empty variable passes one empty argument; $var passes nothing. When an option needs a value, the difference can shift every later argument. Always write [ -n "$var" ] and cmd "$var".
Backslash Inside Single Quotes
Inside single quotes a backslash is literal. Writing 'a\nb' gives backslash-n, not a newline — use $'a\nb' when you want the newline.
$'...' only when you need control characters.