Skip to content
VLSI Mentor

Linux & Bash · Part I · Module 9 · Quoting and Shell Expansion

Double Quotes, Single Quotes and the Backslash

Quoting has no rules of its own. Each of the three tools switches off a specific set of stages in the pipeline, and once you know which, you can work out any case from first principles instead of trying things until one works.

Intermediate18 min readLinuxPart IFundamentalsQuotingExpansion

Part I · Module 9 · Chapter 9.3 · Double Quotes, Single Quotes and the Backslash

Chapter 9.1 said quoting switches pipeline stages off. This is the chapter that says which.

1. Three Tools

The three quoting tools and what each one switches off. Single quotes disable every expansion, so the text is taken exactly as written. Double quotes keep variables, command output and arithmetic, and switch off word splitting, file-name matching, braces and the tilde. A backslash switches off whatever single character follows it.Single quoteseverything literal — noexpansion of any kindDouble quoteskeep variables and commandoutput; stop splitting andmatchingA backslashswitches off exactly the onecharacter after it12
Figure 1 — the three quoting tools and what each one switches off. Single quotes disable every expansion, so the text between them is taken exactly as written. Double quotes are selective: they keep variables, command output and arithmetic, while switching off word splitting, file-name matching, braces and the tilde. A backslash switches off whatever single character follows it, and is the right tool when only one character is the problem.

2. The Table That Answers Everything

Read it as "does this stage still happen?"

Pipeline stageBare"double"'single'
1 — Brace expansionyesnono
2 — Tilde expansionyesnono
3 — Variables $nameyesyesno
3 — Command output $(…)yesyesno
3 — Arithmetic $((…))yesyesno
4 — Word splittingyesnono
5 — File-name matchingyesnono

Three columns, and everything in this module follows from them:

  • Single quotes switch off every stage. What you typed is what the command gets.
  • Double quotes switch off four of the seven rows and keep the three that substitute values.
  • Bare text gets all of them, which is why bare text is dangerous.

3. Seeing the Table Work

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ v=world

$ printf '  double [%s]\n' "$v and $HOME"
  double [world and /home/engineer]

$ printf '  single [%s]\n' '$v and $HOME'
  single [$v and $HOME]

$ printf '  backsl [%s]\n' \$v
  backsl [$v]

Three ways to handle the same text, three different answers — all predictable from the table.

What survives inside double quotes

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ v=X
$ echo "dollar=$v  backtick=`echo B`  arith=$((1+1))  cmdsub=$(echo C)"
dollar=X  backtick=B  arith=2  cmdsub=C

All four substituting forms fired. Note the backtick: it is still active inside double quotes, which is one more reason Chapter 8.4 said never to write it — a stray backtick in a quoted string runs a command.

What does not

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ echo "glob: *.txt  brace: {1..3}  tilde: ~"
glob: *.txt  brace: {1..3}  tilde: ~

None of the three expanded. Compare bare:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ echo glob: *.txt brace: {1..3} tilde: ~
glob: a.txt b.txt two words.txt brace: 1 2 3 tilde: /home/engineer

4. Single Quotes

Single quotes are the strongest and the simplest: everything between them is literal. There are no escapes, no exceptions, and no way to interpret anything.

They are the right tool whenever the text contains characters the shell would otherwise act on:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
grep 'ERROR.*timeout' run.log           # regex metacharacters, untouched
awk '{ print $1, $3 }' results.txt      # $1 is awk's, not the shell's
echo 'Total: $500'                      # a literal dollar sign
sed 's/old/new/g' file                  # slashes and all

That awk line is the clearest case. $1 means something to awk and something else to the shell, and single quotes are how you make sure awk is the one that gets it. With double quotes the shell would substitute its own $1 first — usually empty — and awk would receive { print , }.

5. The Backslash

A backslash switches off the next single character.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ echo \$HOME
$HOME

$ echo two\ words
two words

$ echo \*
*

It is the right tool when exactly one character is the problem and quoting the whole string would be heavy-handed — particularly a single space in an otherwise ordinary word.

Inside double quotes, it is selective

This is the part that surprises people. Within double quotes, a backslash only escapes the characters that are still special there — $, `, ", \ and a newline. Before anything else, it stays as a literal backslash:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ echo "a \$ b"
a $ b

$ echo "a \n b"
a \n b

The second one did not produce a newline. \n is not special to the shell at all — it is a convention that printf and echo -e understand, and inside double quotes the backslash simply had nothing to escape, so both characters survived.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ printf 'a\nb\n'
a
b

printf interprets the escape; the shell does not. Mixing those up produces a surprising number of bug reports.

At the end of a line

A backslash as the very last character continues the line:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
./run_sim.sh \
  --seed 7 \
  --output "$logdir/run.log" \
  --verbose

This is purely cosmetic — the shell joins the lines before doing anything else — but it is how long commands stay readable. Nothing may follow the backslash, not even a space; a trailing space after it means it escapes the space and the line does not continue.

6. The Fourth Form: $'…'

Bash has one more, for when you genuinely need a control character:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ printf '%s' $'a\tb'
a	b

$ echo $'line1\nline2'
line1
line2

$'…' is like single quotes, except that backslash escapes are interpreted — \n, \t, \\, \' and the numeric forms.

It is the clean way to get a tab into a variable:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tab=$'\t'
IFS=$'\n'

That second line is a common sight: splitting on newlines only, so that spaces inside values survive. It is still a global IFS change with all the caveats from Chapter 9.2, but it is the correct spelling of the intent.

7. Choosing

A short decision procedure that covers essentially everything.

What you haveUse
A value from a variable or commandDouble quotes — "$var"
Literal text with $, *, backticks, regexSingle quotes — 'ERROR.*'
A format for another program (awk, sed, find)Single quotes
Mostly literal, but one value must be insertedDouble quotes
One awkward character in an otherwise bare wordBackslash
A real tab or newline in a value$'…'
A path starting with ~Leave the tilde bare, or use "$HOME/…"

And the general shape when you need both in one argument — which is most real commands:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
grep 'ERROR' "$logfile"
awk -F, '{ print $2 }' "$results"
find "$dir" -name '*.log' -newer "$marker"
sed "s/VERSION/$ver/" template.txt > out.txt

Each one is single-quoted where the text belongs to the other program, and double-quoted where a shell value is being inserted. That split is the habit worth building: quote by owner. If the characters are meant for awk, sed, find or grep, single-quote them so the shell keeps its hands off; if the value is the shell's, double-quote it.

The find line is worth a second look — -name '*.log' is single-quoted so that find receives the pattern and does its own matching. Unquoted, the shell would match it against files in the current directory at stage five, and find would be handed whatever happened to be there.

8. Reading What You Wrote

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ v="my dir"
$ set -x
$ ls -d $v
+ ls -d my dir
$ ls -d "$v"
+ ls -d 'my dir'
$ set +x

set -x re-quotes the finished arguments for display, so you can see directly how many there are and where their boundaries fell.

For values, printf %q does the same job:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ v="it's a \"test\" file.txt"
$ printf 'raw : %s\n' "$v"
raw : it's a "test" file.txt
$ printf '%%q  : %q\n' "$v"
%q  : it\'s\ a\ \"test\"\ file.txt

The %q form shows the value quoted so that pasting it back into a shell reproduces it exactly. It is ugly on purpose — it is telling you precisely which characters needed protecting.

Practice

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
mkdir -p /tmp/q && cd /tmp/q
touch a.txt b.txt "two words.txt"
v=world

1 — Walk the table. For each row, predict then check:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
echo {1..3};    echo "{1..3}";    echo '{1..3}'
echo ~;         echo "~";         echo '~'
echo $v;        echo "$v";        echo '$v'
echo $(date +%Y); echo "$(date +%Y)"; echo '$(date +%Y)'
echo *.txt;     echo "*.txt";     echo '*.txt'

2 — Prove backticks still work in double quotes.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
echo "today is `date +%F`"

Then convince yourself why that is a reason to avoid them.

3 — Hit the tilde trap.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
echo ~/project
echo "~/project"
echo "$HOME/project"

4 — Discover that a single quote cannot be escaped. Type echo 'don\'t' and press Enter. When you get the > prompt, press Ctrl-C.

5 — Watch the backslash be selective.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
echo "a \$ b"
echo "a \n b"
printf 'a\nb\n'

6 — Protect the other program's syntax.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf 'a,b,c\n' > r.csv
awk -F, '{ print $2 }' r.csv
awk -F, "{ print $2 }" r.csv

The second prints an empty line, because the shell ate $2 before awk saw it.

7 — Use $'…'.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf '%s' $'col1\tcol2'; echo
tab=$'\t'; printf 'a%sb\n' "$tab"

8 — Inspect with %q.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
for s in "plain" "two words" "it's" 'a"b' 'x*y'; do printf '%-12s -> %q\n' "$s" "$s"; done

9 — Clean up. cd /tmp && rm -rf /tmp/q

Check Your Understanding

1. Which stages does a double quote switch off, and which does it leave running?

2. echo "~/logs" prints ~/logs rather than a path. Why, and what are two fixes?

3. Why must awk '{ print $1 }' use single quotes?

4. echo "a \n b" prints a literal backslash-n. Why, and what would produce a newline?

5. Can you put a single quote inside a single-quoted string?

6. find . -name *.log behaves differently depending on what is in the current directory. Why, and what is the fix?

9. Remember

  • Quoting switches pipeline stages off. Which stages is the whole of it.
  • Single quotes switch off everything. No expansions, no escapes, no exceptions.
  • Double quotes keep variables, command output and arithmetic, and switch off braces, tilde, word splitting and file-name matching.
  • Bare text gets every stage, which is why it is the dangerous one.
  • "$var" is the default because it keeps what puts a value in and stops what takes it apart.
  • Backticks still fire inside double quotes — one more reason not to use them.
  • Quotes switch off the tilde. Leave ~ bare, or use "$HOME/…" in scripts.
  • A single quote cannot appear inside single quotes, by any means.
  • A backslash switches off the next character, and inside double quotes only before $, backtick, ", \ and newline — elsewhere it stays literal.
  • \n is printf's escape, not the shell's.
  • A trailing backslash continues a line, and nothing may follow it.
  • $'…' gives real tabs and newlines; it is a Bash extension, and printf is the portable alternative.
  • Quote by owner. Single-quote text meant for awk, sed, find or grep; double-quote values that belong to the shell.
  • set -x and printf %q show you what you actually built.

You can now choose a quote deliberately. The next chapter deals with the case this one deferred twice: what to do when the text you need to quote already contains quotes of its own.

Where this fits

Part of the Linux & Bash curriculum.