Why this matters
A surprising number of characters mean something to bash: space, $, *, ?, ;, &, |, <, >, (, ), quotes, backslash, #, ~, !, newline. File names, user input, passwords and messages contain these all the time. Quoting is how you tell bash "this is data, not syntax". Getting it right is the single most important habit in shell scripting; most shell bugs, and many security holes, are quoting bugs.
The problem quoting solves
When bash reads a line, it first breaks it into tokens using the special characters. A space separates words, ; ends a command, > redirects, $ starts an expansion, * is a wildcard. Try:
$ echo Price: $5; total
Price:
bash: total: command not found
Two things went wrong. $5 is a special parameter (the fifth argument of a script, empty here) so it expanded to nothing. The ; ended the echo command, and total was run as a second command. Quoting fixes both:
$ echo 'Price: $5; total'
Price: $5; total
Three ways to quote
Single quotes: everything literal
Everything between ' and the next ' is taken literally. No expansion, no escapes, nothing:
$ echo '$HOME is where `the` heart is; * \n'
$HOME is where `the` heart is; * \n
The one character you cannot put inside single quotes is a single quote, because the next ' always ends them, and a backslash there is just a backslash.
Double quotes: literal, except for $, backtick and backslash
Inside "..." spaces and most special characters lose their meaning, but expansions still happen: $variable, ${...}, $(command), $((arithmetic)) and the old backtick form. A backslash is special only before $, `, ", \ or a newline.
$ name=Ada
$ echo "Hello, $name; you have * new messages"
Hello, Ada; you have * new messages
$ echo "Say \"hi\" and pay \$5"
Say "hi" and pay $5
$ echo "back\slash"
back\slash
The * stayed a star: no file name expansion inside double quotes. The ; did not end the command. But $name was expanded. This makes double quotes the everyday tool for strings that contain variables, and, as lesson 4 shows, the essential tool for using variables at all.
Backslash: one character
Outside quotes, a backslash removes the special meaning of the single next character:
$ echo Price: \$5\; total
Price: $5; total
$ touch my\ file.txt
$ ls
my file.txt
A backslash at the very end of a line escapes the newline, so the command continues on the next line. This is how long commands are split for readability:
tar -czf backup.tar.gz \
--exclude='*.tmp' \
documents/ photos/
(Make sure nothing, not even a space, follows that backslash. \ followed by newline is an escaped space, and the command ends there.)
Mixing quotes in one word
Quoted and unquoted pieces next to each other with no space between them form a single word. This lets you combine styles:
$ echo 'It'"'"'s '"$name"'!'
It's Ada!
$ echo 'It'\''s fine'
It's fine
Read the first as five adjacent pieces: 'It', "'", 's ', "$name", '!'. The second idiom, '\'', means: close the single quotes, an escaped ', reopen. It is ugly but common in scripts that generate shell code.
ANSI-C quoting: $'...'
bash has a fourth form: $'...', where backslash escapes work as in C:
$ echo $'tab:\there\nnew line, and an apostrophe: \''
tab: here
new line, and an apostrophe: '
\t is a tab, \n a newline, \\ a backslash, \' a single quote. It is not POSIX (module 16 covers portability), but it is the clearest way to put a tab or newline into a bash string.
echo itself does not interpret \n by default in bash: echo 'a\nb' prints a\nb. That is one reason many scripts prefer printf, which always does:
$ printf 'a\tb\n'
a b
$ printf '%s has %s files\n' "$name" 3
Ada has 3 files
printf FORMAT ARGS... prints the format, replacing each %s with the next argument. Unlike echo, it adds no newline of its own. You will use it a lot from now on.
Quote removal: what the program actually gets
Quotes are instructions to bash, not data. After bash has used them to decide where words begin and end and what to expand, it deletes them. This last step is literally called quote removal:
you type: touch 'my notes.txt' "$name.log" it\'s
bash parses: word 1 │ word 2 │ word 3 │ word 4
after expansion and quote removal:
argv[0]=touch argv[1]=my notes.txt argv[2]=Ada.log argv[3]=it's
touch receives three file names, none containing any quote character. Programs never see your quotes (unless you quote a quote, as in "say \"hi\""). This also means quoting is a shell concept: once bash has built the argument list and called execve, quoting no longer exists.
Comments and other characters
An unquoted # at the start of a word begins a comment that runs to the end of the line:
$ echo one # two three
one
$ echo one#two
one#two
$ echo 'one # two'
one # two
In an interactive bash, ! inside double quotes can trigger history expansion (!! means "the last command"). If echo "Hello!" behaves oddly in your terminal, use single quotes. Scripts do not have history expansion, so this only bites interactively.
Common mistakes
- Unclosed quote. bash shows the
>continuation prompt and waits. Press Ctrl-C and retype. - Single quotes around a variable you wanted expanded.
echo 'Hello, $name'prints the dollar sign. Use double quotes. - Backslash inside single quotes.
'It\'s'does not work; inside'...'the backslash is literal. echo "Total: $5".$5is a parameter, almost always empty: you getTotal:. Escape it (\$5) or use single quotes.- Smart quotes copied from a web page or word processor.
'and"that look curly are not quote characters to bash. You get errors likecommand not foundwith odd characters in them.
Exercises
Do the quizzes in your head first, then try each line in the shell to confirm. The code exercise is compared character for character, so every quote and space counts.
Recap
- Single quotes: everything literal; cannot contain
'. - Double quotes: literal except
$, backtick and backslash; variables still expand; no globbing. - Backslash: protects the next character; at the end of a line, continues the command.
$'...': C-style escapes such as\n,\t,\'.- Adjacent quoted pieces form one word; quotes are removed before the program runs.
printf 'format\n' argsis more predictable than echo.
Going further: run help printf and try printf '%5s|%-5s|\n' ab cd. Then use printf '[%s]\n' ... to inspect exactly which arguments a line produces: printf '[%s]\n' one "two three" 'four'"five".