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

EscapeMeaningExample
\nNewline$'a\nb'
\tTab$'a\tb'
\\Literal backslash$'a\\b'
\'Literal single quote$'it\'s'
\xNNHex 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.

Rule of thumb: use double quotes for variables and command substitution, single quotes for literal patterns, and $'...' only when you need control characters.