In a bash script, what is the difference between wrapping text in single quotes and wrapping it in double quotes, and which shell behaviours does each one turn off?
answer
- one of them still expands things
- which stage does each quote suppress
- expansion kept, splitting and globbing off
- nothing is special inside these
- no escape character exists in the literal form
basics
~20 sDouble quotes still expand variables and command substitutions while suppressing word splitting and filename globbing; single quotes suppress all of that as well, so every character between them is literal, including the dollar sign and the backslash.
solid answer
~40 sBoth forms stop the shell from splitting the text into several words and from expanding globs, but they differ in what still happens inside. In double quotes, parameter expansion (`$var`, `${var}`), command substitution (`$(cmd)`) and arithmetic expansion (`$((1+1))`) all still run — what is switched off is word splitting on `IFS` and pathname expansion, which is why `"$file"` is always exactly one argument no matter what it contains. In single quotes nothing is special at all: `'Hello $name'` prints the dollar sign and the name literally, and there is no escape character, so a single quote cannot appear inside single quotes even with a backslash. A useful shorthand: double quotes mean "expand it, but keep it as one word"; single quotes mean "hands off entirely".
code
bash · 8 linesname=World
echo "Hello, $name" # Hello, World
echo 'Hello, $name' # Hello, $name
file='My Report.txt'
: > "$file" # creates exactly one file
ls -1 "$file" # My Report.txt
rm -- "$file"go deeper
Be ready to say plainly that double quotes still expand variables while single quotes make everything literal, and that both stop a value with spaces from becoming two arguments. Quote your expansions in any code you write on a whiteboard.
Explain the mechanics: which expansions survive inside double quotes, that word splitting and globbing are what quoting suppresses, and that the backslash only escapes five characters inside double quotes.
Show the production instinct — single quotes for anything another tool will parse (awk programs, regexes, find patterns), double quotes for everything else, and the ability to spot the unquoted expansion in a review before ShellCheck does.
Frame it as a policy question: an unconditional quote-everything rule enforced by ShellCheck in CI is cheaper than teaching each engineer the exceptions, and a script that wants splitting should be using an array instead.
## What quoting is really doing Quoting in bash is not a way of "making a string" the way it is in most programming languages. Bash reads a line, then runs a series of expansions over it, and finally hands the resulting words to a command as separate arguments. Quoting marks characters so that some of those stages leave them alone. That is why the interesting question is never "does this make a string" but "which stage does this quote switch off". Two of those stages cause almost all real bugs: **word splitting**, where an unquoted expansion is chopped into several words at spaces, tabs and newlines, and **pathname expansion** (globbing), where characters like `*`, `?` and `[` are replaced by matching filenames. Both quote forms switch both of those off. The difference between them is what still runs *inside* the quotes. ## Double quotes: expansion yes, splitting and globbing no Inside `"..."` the shell still performs: - parameter expansion — `$name`, `${name}`, `${name:-default}` - command substitution — `$(date)` and the older backtick form - arithmetic expansion — `$((count + 1))` What it no longer does is split the result on `IFS` or expand `*` against the filesystem: ```bash file='My Report.txt' ls -l $file # two arguments: 'My' and 'Report.txt' -> two errors ls -l "$file" # one argument: 'My Report.txt' ``` The backslash keeps a limited special meaning inside double quotes. It escapes only `$`, a backtick, `"`, another backslash, and a newline. Before anything else it is just a literal backslash — `echo "a\tb"` prints `a\tb`, not a tab, because `\t` means nothing to the shell. Producing a real tab is `printf`'s job, or the `$'...'` form. A few characters that people expect to be neutralised are not: `"` obviously ends the quote, and history expansion with `!` still fires inside double quotes in an interactive shell (in a script, history expansion is off, so this is not a scripting concern). ## Single quotes: completely literal Inside `'...'` no character is special. There is no expansion and there is no escape character: ```bash name=World echo "Hello, $name" # Hello, World echo 'Hello, $name' # Hello, $name echo 'a\tb' # a\tb - backslash is literal too ``` The consequence people get caught by is that a single quote cannot be escaped inside single quotes. `'it\'s'` does not work; the quote simply ends at the second `'`. The standard trick is to close the quote, emit an escaped quote, and reopen: `'it'\''s'`, or just use double quotes when the text contains an apostrophe but no `$` or backtick. Single quotes are the right choice whenever you are writing text that another program will interpret: an `awk` or `sed` program, a `grep` regular expression, a `find` pattern that `find` itself should expand, a JSON body, a password with `$` in it. In all of those cases you want bash to hand the characters over untouched. ```bash grep '^\$[0-9]' prices.txt # bash passes ^\$[0-9] straight to grep find . -name '*.log' # find does the matching, not bash ``` If you had written `find . -name *.log`, bash would expand the glob first against the current directory, and `find` would receive whatever filenames happened to match — a classic source of "it worked on my machine". ## Quote removal After all expansions are done, bash strips the quote characters themselves so the command never sees them. That is why `ls -l "$file"` passes `My Report.txt` and not `"My Report.txt"` — the quotes are shell syntax, not part of the data. It also means you cannot add quotes by putting them in a variable: the contents of a variable are data, and data is not re-scanned for quoting. ## The practical rule Double-quote every expansion by default. If you find yourself wanting an expansion unquoted so it splits into several words, that is almost always a sign you want an array instead. Use single quotes for literal text that must reach another program unchanged. ShellCheck flags the unquoted case as `SC2086`, which is by a wide margin the most common finding in real shell code.
- How do you put a literal single quote inside a single-quoted string?You cannot — there is no escape character inside single quotes, so the quote just ends the string. Close it, escape a quote outside, and reopen: `'it'\''s'`. If the text has no `$`, backtick or backslash, using double quotes (`"it's"`) is simpler and more readable.
- Why does `echo "a\tb"` print a backslash and a t instead of a tab?Inside double quotes the backslash only escapes `$`, a backtick, `"`, `\` and a newline; before a `t` it is an ordinary character, so bash passes `a\tb` through. Interpreting `\t` is the job of `printf 'a\tb\n'`, or of the `$'a\tb'` ANSI-C quoting form. `echo -e` also does it but is not portable.
- Does quoting change anything when the variable holds a single word with no special characters?Not for that value — but the quotes are protection against values you did not predict. A path that gains a space, an empty variable that would otherwise vanish and shift every later argument, or a value containing `*`. Quoting costs nothing and removes a whole class of bugs, which is why the habit is unconditional.
saying these in an interview costs you the question
- Says single and double quotes are interchangeable in bash
- Thinks double quotes make the shell treat the value literally
- Believes a backslash can escape a quote inside single quotes
- Expects \t or \n inside double quotes to become real whitespace
- Claims quoting is only needed for values containing spaces