| Tested on | RHEL 10.2 (Coughlan) |
|---|---|
| Package | bash 5.2.26-6.el10 |
| Applies to | Linux distributions with Bash installed |
| Privilege | Normal user; no root required |
| Scope | Single quotes, double quotes, backslash escaping, dollar-single-quoting $'...', variable and filename safety, and how quoting changes word splitting and [[ ]] pattern tests. Does not cover the complete shell expansion order or =~ regex quoting rules in depth. |
| Related guides | Bash shell script tutorial Shell scripting course hub Linux command line basics |
This article compares Bash single quotes and double quotes using the same strings in a terminal, showing what Bash expands and what remains literal.
Use single quotes when the text must remain literal. Use double quotes when variables or command substitutions should expand but the resulting value should stay together as one argument.
Quoting changes how Bash interprets text before passing it to a command. With paths, variables, and wildcard patterns, the choice between quoted and unquoted text can change how many arguments a command receives or which filenames it operates on.
Why Quoting Changes Bash Behavior
Bash does not pass your typed line directly to a command. It performs several parsing and expansion steps first, including brace expansion, tilde expansion, parameter expansion, arithmetic expansion, command substitution, process substitution, word splitting, filename expansion (globbing), and quote removal.
Quoting controls which of those steps still run on a piece of text. That is why '$HOME' prints a dollar sign and the letters HOME, while "$HOME" prints your home directory path. Single quotes disable shell expansions, while double quotes still allow parameter expansion, arithmetic expansion, and command substitution.
Backslash Escaping
A backslash outside quotes removes the special meaning of the next character. Inside double quotes, backslash only special-cases a few characters ($, `, ", \, and newline); other backslashes are often left alone.
Print a literal $HOME without quotes by escaping the dollar sign:
echo \$HOME$HOMEInside double quotes, escaping $ also stops variable expansion:
echo "\$HOME"$HOMEA backslash before a newline inside double quotes joins the lines and drops the newline from the output:
echo "line1\
line2"line1line2Use a backslash when you only need to protect one character instead of quoting a whole string.
Single Quotes
Everything between single quotes is treated literally. Variables, command substitutions, and glob characters such as * and ? are not expanded. A literal single quote cannot appear inside the same single-quoted segment.
Compare single-quoted $HOME with an unquoted expansion:
echo '$HOME'$HOMEWithout quotes, the same variable expands to your home directory path:
echo $HOME/root(Your path will differ; the point is that the unquoted form expands.)
Glob characters stay literal inside single quotes:
echo '*.txt'*.txtCreate two sample files with spaces in their names so globbing has something to match:
printf 'sample\n' > 'Quarterly Report Q1.txt' && printf 'sample\n' > 'Quarterly Report Q2.txt'Outside quotes, the same pattern expands to matching filenames in the current directory:
echo *.txtQuarterly Report Q1.txt Quarterly Report Q2.txtDouble Quotes
Double quotes still allow parameter expansion, command substitution, and arithmetic expansion. They prevent word splitting and pathname expansion on the quoted text, but $, backticks, and $(...) remain active.
Store a path in a variable and expand it inside double quotes:
echo "Home is $HOME"Home is /rootCommand substitution runs inside double quotes:
echo "Working in $(pwd)"Working in /tmp/quote-labThe same command substitution is literal inside single quotes:
echo 'Working in $(pwd)'Working in $(pwd)Wildcards do not expand inside double quotes. The asterisk is printed literally:
echo "*.txt"*.txtIn an interactive Bash session with history expansion enabled, ! can also be special inside double quotes. This usually does not affect non-interactive shell scripts, where history expansion is normally disabled.
Single vs Double Quotes — Expansion Table
The table summarizes what Bash still processes inside each quoting style. “Disabled” means the step does not run on characters inside the quotes.
| Construct | Single quotes '...' |
Double quotes "..." |
Unquoted |
|---|---|---|---|
Variable $var |
Literal $var |
Expands | Expands |
Command substitution $(cmd) |
Literal text | Runs command | Runs command |
Arithmetic $((n+1)) |
Literal text | Expands | Expands |
Glob *, ?, [ |
Literal characters | Literal characters | Filename expansion |
| Whitespace from variable expansion | Kept literally | Preserved within one argument | May undergo word splitting |
Backslash \ |
Literal backslash (no escapes) | Escapes $, `, ", \, newline |
Escapes next character |
Quote removal itself happens after those expansions. You type the quotes; Bash uses them during parsing and then strips them before the command runs.
Dollar-Single-Quoting $'...' and ANSI-C Escapes
Bash supports $'...' quoting (GNU Bash calls this ANSI-C quoting). The dollar sign before the opening single quote switches to a mode where backslash sequences such as \n, \t, and \\ are interpreted.
Print two lines with an embedded newline:
echo $'line1\nline2'line1
line2Tab and other C-style escapes work the same way:
echo $'col1\tcol2'col1 col2Use $'...' when a string needs escape sequences such as \n, \t, or an escaped character. Dollar-single-quoting is standardized in POSIX.1-2024, but older POSIX shells may not support it, so check the target shell when portability to older systems matters.
How to Put Quote Characters Inside Strings
You cannot escape a single quote inside a single-quoted string with \'. The backslash is literal there, not an escape.
The portable pattern is to end the quoted segment, add an escaped single quote outside quotes, and open a new single-quoted segment:
echo 'Don'\''t panic'Don't panicBash closes the first single-quoted string, inserts an escaped apostrophe outside the quotes, and then starts another single-quoted string. That is what 'Don'\''t panic' does: 'Don' + \' + 't panic'.
When double quotes are acceptable, an apostrophe needs no special trick:
echo "Don't panic"Don't panicFor bash concatenate strings across quoted pieces, the same segment rule applies: 'part one'"$variable"'part two' is valid because adjacent quoted strings become one word.
Escape a double quote inside a double-quoted string with \":
echo "She said \"hello\""She said "hello"Quoting Variables and Filenames Safely
A variable with spaces must stay quoted, or word splitting breaks it into multiple arguments:
names='foo bar'
echo $namesfoo barBoth words appear on one line, but echo received two arguments. Use printf to show how many arguments Bash actually produced:
printf '<%s>\n' $names<foo>
<bar>Double quotes keep the value as one argument:
printf '<%s>\n' "$names"<foo bar>Filenames with spaces and globs
Create a small temp directory with spaced names and a glob in a variable:
mkdir -p /tmp/quote-lab/safe-rm && cd /tmp/quote-lab/safe-rm && touch 'report one.txt' 'report two.txt' && file='report *.txt' && ls -1report one.txt
report two.txtUnquoted $file can first split into words and then expand wildcard patterns:
printf '%s\n' $filereport
report one.txt
report two.txtQuoted "$file" is passed as one argument containing the literal *:
printf '%s\n' "$file"report *.txtAs a general rule, quote variables that contain filenames or paths unless you specifically need shell splitting or glob expansion.
Quoted vs Unquoted Patterns in [[ ... ]]
Inside [[ ... ]], an unquoted right-hand side is a pattern, not always a literal string. That matters for Bash if else tests on filenames.
Set a sample log filename and test with an unquoted pattern:
file='app.log'
[[ $file == *.log ]] && echo 'pattern match'pattern matchQuote the pattern on the right-hand side and Bash compares literally — *.log is no longer a glob:
[[ "$file" == "*.log" ]] && echo 'literal match' || echo 'literal: no match'literal: no matchRegex matching with =~ has different right-hand-side quoting rules, so do not assume it behaves exactly like ==. For this comparison, the important point is that quoting can change whether pattern characters are interpreted or treated literally.
"$@" and Argument Lists
"$@" is a special case. It expands to the positional parameters as separate quoted words — one per argument, even when an argument contains spaces.
Loop with unquoted $@ inside a script (word splitting breaks arguments):
bash -c 'for a in $@; do echo "unquoted: >$a<"; done' _ one "two words" threeunquoted: >one<
unquoted: >two<
unquoted: >words<
unquoted: >three<Quoted "$@" preserves each original argument:
bash -c 'for a in "$@"; do echo "quoted: >$a<"; done' _ one "two words" threequoted: >one<
quoted: >two words<
quoted: >three<Use "$@" when forwarding script arguments to another command unchanged.
Common Quoting Bugs
These mistakes show up often in scripts and one-liners:
- Unquoted
$varwith spaces — splits into multiple arguments; breakscp,rm, andmvon paths with spaces. - Double quotes around a glob you meant to expand —
"$pattern"disables pathname expansion on that word; a literal*reaches the command. - Single-quoted text containing an apostrophe —
'it's fine'ends the single-quoted string early; use the'\''technique, double quotes, or$'...'when appropriate. - Assuming
echo $varshows the real value — word splitting and globbing can change whatechoreceives; useprintf '%s\n' "$var"when debugging. - Mixing pattern and literal tests in
[[ ]]—[[ $f == *.log ]]and[[ "$f" == "*.log" ]]answer different questions.
Where Quotes Are Not Required
Quoting variables is the safe default, but quotes are not always needed:
for word in $nameswhen you want word splitting to iterate over separate tokens — an array is usually clearer when the input format is not guaranteed.- Inside
[[ ... ]]tests — parameter expansions such as[[ -n $var ]]do not need quotes to prevent word splitting or globbing. Quoting can still be used for consistency, but quoting the right-hand side of==can change pattern-matching behavior. - Arithmetic contexts such as
$(( count + 1 ))where the shell parses operators, not words.
If you are unsure, quote the expansion and adjust the script logic explicitly rather than relying on accidental splitting.
Practical Bash Quoting Rules
These rules are heuristics, not exhaustive grammar:
- Use single quotes for fixed text that contains shell metacharacters or should remain completely literal, such as
$HOME,*.txt, or a fixed regex fragment you will not expand. - Use double quotes around variable and command substitutions that should expand as one word:
"$file","$HOME/bin","$(date)". - Use
$'...'when you need\n,\t, or other C escapes in one word. - Quote variable expansions that should be passed as a single argument, especially filenames and paths.
- In
[[ ]], decide whether the right-hand side is a pattern (often unquoted) or a literal string (quoted). - When debugging,
printf '%s\n' "$var"shows the value Bash actually passes to commands.
Summary
Bash single quotes and double quotes are not interchangeable labels for “strings.” Single quotes disable shell expansions; double quotes still allow $, command substitution, and arithmetic, while blocking word splitting and globbing on the quoted text. Backslash escaping and $'...' dollar-single-quoting add two more ways to control what stays literal.
The examples above show why quoting matters for filenames with spaces, wildcard expansion, variable arguments, and pattern tests inside [[ ... ]]. Command substitution and variables inside double quotes behave predictably once you know which parsing steps still run.
For day-to-day scripting, quote variable expansions that should stay one argument, pick single quotes for fixed literals, and reach for $'...' when you need escape sequences. When a test surprises you, check whether the right-hand side of [[ ]] was quoted as a literal string or left unquoted as a pattern.
References
- GNU Bash manual — Quoting
- GNU Bash manual — ANSI-C Quoting
- GNU Bash manual — Shell Expansions
- POSIX Shell Command Language — Quoting
Frequently Asked Questions
Can I escape ' inside '...'?
No. Inside a single-quoted string, backslash has no escape function, and a literal single quote cannot be included without ending the quoted string first. To include an apostrophe, end the segment, write \' outside quotes, and continue: 'Don'\''t'. Double quotes or $'...' are often simpler when the string allows them.
Why does "$var" differ from $var?
Unquoted $var is subject to word splitting and pathname expansion after variable expansion. Double-quoted "$var" expands the variable while preserving its value as one argument. For filenames, paths, and values that should remain one shell argument, "$var" is the usual default.
Do wildcards expand inside double quotes?
No. Characters like * and ? are ordinary characters inside double quotes. Globbing runs only on unquoted words during pathname expansion. That is why "*.txt" prints a literal asterisk.
What is $'...'?
It is dollar-single-quoting (GNU Bash also calls it ANSI-C quoting). The $' prefix before a single-quoted string enables backslash escapes such as \n and \t. POSIX.1-2024 standardizes this form, but older shells may not support it.

