Unquoted variables are the single most common source of shell bugs, from broken file names to deleted data.
After bash expands an unquoted variable, it does two more things to the result: word splitting (cutting it at spaces, tabs and newlines) and globbing (treating *, ? and [...] as file-name patterns). That is why rm $file with file="my report.txt" tries to delete two files, my and report.txt.
Quotes switch that off:
- Double quotes
"..."keep the text as one word but still expand$var,$(command)and$((math)). Use them around nearly every expansion:"$file","$@". - Single quotes
'...'are completely literal; nothing inside is expanded, and a single quote cannot appear inside them. - A backslash escapes the next character:
echo \$HOMEprints$HOME. $'...'(ANSI-C quoting) understands escapes like\nand\t.
Quotes can be mixed in one word: 'It'\''s' or "$name"'s file'. They are removed before the command sees its arguments, so echo "a"b'c' receives the single argument abc.
The rule of thumb is simple: quote every expansion unless you deliberately want splitting or globbing. Inside [[ ]] and on the right of a plain assignment, bash does not split, but quoting there is harmless too.
Example
cd "$(mktemp -d)"
file="my report.txt"
touch "$file"
ls $file # ls: cannot access 'my' ... and 'report.txt' (two words!)
ls "$file" # my report.txt
echo '$HOME is literal' # $HOME is literal
echo "Home is $HOME" # Home is /home/you
echo \$HOME # $HOME
echo $'col1\tcol2' # col1 col2
star="*"
echo $star # my report.txt (the * was globbed!)
echo "$star" # *
echo "a"b'c' # abc (quotes are removed, pieces join)Remember
- Unquoted expansions are split on whitespace and globbed
- Double quotes: one word, variables still expand; single quotes: fully literal
- Default to "$var" and "$@" everywhere
Watch out for
- Writing for f in $(ls) or rm $dir/* with unquoted variables that can contain spaces or be empty.