Making the shell leave your text alone
◈ 9 cardsWhat the shell rewrites before a command ever runs, and how single quotes, double quotes and the backslash each switch those rewrites off.
The shell edits your command line before the command runs
Type a line at a shell prompt and the program named at the front of it is not the first thing to touch it. The shell reads the whole line, splits it into words, and then rewrites those words: it substitutes variables, runs command substitutions, expands filename patterns, removes the quotes it used along the way, and only then hands a finished argument list to the program. Every character that triggers one of those rewrites is a metacharacter, and quoting is how you switch a rewrite off.
This is not a tidiness concern. A filename containing a space is two words to an unquoted shell. A filename containing an asterisk is a pattern. A file called tryit& is a command followed by a background operator. In each case the program you named receives an argument list you never intended, and the error it prints describes its confusion rather than yours — which is why quoting bugs are so consistently misdiagnosed.
Worked example — one variable, four spellings
Ada's home directory is /home/ada. Here are four ways to write the same five characters after echo, and four different results.
echo $HOME
echo "$HOME"
echo '$HOME'
echo \$HOME
The first prints /home/ada. The shell saw an unquoted dollar sign, looked up the variable, replaced the word with its value, and then split that value into words — here there is only one word, so nothing more happens.
The second also prints /home/ada, but for a different reason worth holding on to. Double quotes still allow the substitution, and then guarantee the result stays one word no matter what it contains. If the variable's value held a space, the first form would pass two arguments and the second would pass one. That difference is the whole reason experienced people write the quotes even when they look redundant.
The third prints the five characters $HOME. Single quotes turn off every rewrite between them, so the shell never even recognises a variable there.
The fourth also prints $HOME. A backslash quotes exactly one character — the dollar sign — and leaves the rest of the word alone. Reach for it when you need to disarm a single character in the middle of something you otherwise want expanded.
The rule that is worth memorising
Single quotes protect everything between them, with one consequence: you cannot put a single quote inside single quotes at all, because the first one you type ends the run. Double quotes protect everything except four things — the dollar sign, the backquote, the backslash, and the closing double quote itself. Those four keep working inside double quotes, which is exactly what makes double quotes useful and exactly what makes them leak.
So the practical rule: going from double quotes to single quotes can only ever protect more, never less. Going the other way can silently switch a substitution back on.
Making the shell leave a filename alone
Someone has left you a file literally named M*A*S*H. Typing cat M*A*S*H does not open it: the shell treats each asterisk as a pattern character, looks for names beginning with M, containing A then S then ending in H, finds the file, and — this time — happens to hand cat the right name anyway. It works by luck. Add one more file to the directory and it breaks. Three spellings work on purpose instead:
cat 'M*A*S*H'
cat "M*A*S*H"
cat M\*A\*S\*H
All three are correct here, because the asterisk is not one of the four characters that survive double quotes.
Now the harder one. A file is named tryit& and you want to rename it to tryit. Typing mv tryit& tryit does not fail with a message about the file — the shell splits the line at the ampersand first, starts mv tryit in the background (where mv complains about a missing destination), and then tries to run tryit as a command. You get two unrelated errors and neither mentions quoting. mv 'tryit&' tryit is the fix, and so is mv tryit\& tryit.
The habit to build: when a command behaves as though it were given different arguments than you typed, it was. Put the argument in single quotes and run it again — if the behaviour changes, the shell was rewriting it.