Bash Single vs Double Quotes: Differences and Examples

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
Short on time?Beta Get the gist of this 10-minute read in seconds, then ask follow-up questions

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:

bash
echo \$HOME
output
$HOME

Inside double quotes, escaping $ also stops variable expansion:

bash
echo "\$HOME"
output
$HOME

A backslash before a newline inside double quotes joins the lines and drops the newline from the output:

bash
echo "line1\
line2"
output
line1line2

Use 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:

bash
echo '$HOME'
output
$HOME

Without quotes, the same variable expands to your home directory path:

bash
echo $HOME
output
/root

(Your path will differ; the point is that the unquoted form expands.)

Glob characters stay literal inside single quotes:

bash
echo '*.txt'
output
*.txt

Create two sample files with spaces in their names so globbing has something to match:

bash
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:

bash
echo *.txt
output
Quarterly Report Q1.txt Quarterly Report Q2.txt

Double 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:

bash
echo "Home is $HOME"
output
Home is /root

Command substitution runs inside double quotes:

bash
echo "Working in $(pwd)"
output
Working in /tmp/quote-lab

The same command substitution is literal inside single quotes:

bash
echo 'Working in $(pwd)'
output
Working in $(pwd)

Wildcards do not expand inside double quotes. The asterisk is printed literally:

bash
echo "*.txt"
output
*.txt

In 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:

bash
echo $'line1\nline2'
output
line1
line2

Tab and other C-style escapes work the same way:

bash
echo $'col1\tcol2'
output
col1	col2

Use $'...' 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:

bash
echo 'Don'\''t panic'
output
Don't panic

Bash 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:

bash
echo "Don't panic"
output
Don't panic

For 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 \":

bash
echo "She said \"hello\""
output
She said "hello"

Quoting Variables and Filenames Safely

A variable with spaces must stay quoted, or word splitting breaks it into multiple arguments:

bash
names='foo bar'
echo $names
output
foo bar

Both words appear on one line, but echo received two arguments. Use printf to show how many arguments Bash actually produced:

bash
printf '<%s>\n' $names
output
<foo>
<bar>

Double quotes keep the value as one argument:

bash
printf '<%s>\n' "$names"
output
<foo bar>

Filenames with spaces and globs

Create a small temp directory with spaced names and a glob in a variable:

bash
mkdir -p /tmp/quote-lab/safe-rm && cd /tmp/quote-lab/safe-rm && touch 'report one.txt' 'report two.txt' && file='report *.txt' && ls -1
output
report one.txt
report two.txt

Unquoted $file can first split into words and then expand wildcard patterns:

bash
printf '%s\n' $file
output
report
report one.txt
report two.txt

Quoted "$file" is passed as one argument containing the literal *:

bash
printf '%s\n' "$file"
output
report *.txt

As 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:

bash
file='app.log'
[[ $file == *.log ]] && echo 'pattern match'
output
pattern match

Quote the pattern on the right-hand side and Bash compares literally — *.log is no longer a glob:

bash
[[ "$file" == "*.log" ]] && echo 'literal match' || echo 'literal: no match'
output
literal: no match

Regex 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
bash -c 'for a in $@; do echo "unquoted: >$a<"; done' _ one "two words" three
output
unquoted: >one<
unquoted: >two<
unquoted: >words<
unquoted: >three<

Quoted "$@" preserves each original argument:

bash
bash -c 'for a in "$@"; do echo "quoted: >$a<"; done' _ one "two words" three
output
quoted: >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 $var with spaces — splits into multiple arguments; breaks cp, rm, and mv on 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 $var shows the real value — word splitting and globbing can change what echo receives; use printf '%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 $names when 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


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.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with 15+ years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)