Basics

Single or double quotes in the terminal — and where your $5 went

Illustration: two jars — the sealed one keeps its cards as they are, while coins pour into the open one and replace some of the cards

Try this in a terminal:

echo "Sale $5 today"

You get Sale today. Five dollars vanished. No error, no warning.

Now the same with single quotes: echo 'Sale $5 today'. Everything's there.

In Python or JavaScript there's almost no difference between ' and ". In the terminal they're two different tools. Mix them up — and the command does the wrong thing, silently.

The main difference in one sentence

  • Single quotes '...' — "take it literally". Nothing happens inside: $, !, \, backticks are just characters.
  • Double quotes "..." — "glue into one string, but fill in values". Inside, $VARIABLE, $(command) and, in an interactive terminal, ! all work.

Both protect against splitting on spaces. That part is shared. The difference is what happens to special characters inside.

Point-by-point comparison

$ — variable substitution.

  • '...': stays a character. '$HOME' is five characters.
  • "...": substitutes the value. "$HOME" is /home/me.

That's where the dollars went: $5 is the script's fifth argument. In your terminal it's empty, so empty got substituted.

$(...) — command output.

  • '...': not executed.
  • "...": executed. "Today $(date +%F)" inserts the date.

! — history recall.

  • '...': just an exclamation mark.
  • "...": in interactive bash and zsh it acts as a history command. echo "Done!!" prints not "Done!!" but "Done" plus your previous command. A single ! at the end of a string is usually safe; !! and !word are not.

\ — escaping.

  • '...': the backslash is an ordinary character. You can't escape anything at all inside single quotes.
  • "...": escapes four characters: $, ", \ and the backtick. "price \$5" saves the dollars.

Spaces.

  • Both protect equally: "my file.txt" and 'my file.txt' are one argument, not two. How names with spaces break without quotes is covered separately — why a script breaks on spaces.

Where it bites in real work

Commits. git commit -m "Fixed $PATH in the config" — and your whole PATH goes into history instead of the word. Write messages with $ and ! in single quotes. Same for agents: when Claude Code or Codex builds a command containing text, this is where the message most often gets mangled.

JSON in curl. JSON is full of double quotes inside, so the outside needs single ones:

curl -X POST https://api.example.com/users \
  -H 'Content-Type: application/json' \
  -d '{"name": "Ann", "role": "admin"}'

But if you need to insert a variable, it won't work inside single quotes. The trick: close the single quotes, put the variable in double quotes, open again:

-d '{"name": "'"$NAME"'"}'

Looks like Morse code, but it works: three pieces glue into one string. When the JSON has more than a couple of fields, it's safer to build it with jq -n --arg name "$NAME" '{name: $name}' or a heredoc.

Regexes, awk, sed. They're full of $ and \ that must reach the program untouched. Almost always single quotes: awk '{print $2}'. In double quotes, $2 turns into nothing before awk even sees it.

Variables in scripts. Here it's the opposite: "$FILE" and "$@" always go in double quotes. The value gets substituted, and spaces in it won't split the argument.

How to put a single quote inside single quotes

You can't — escaping doesn't work inside. But there are two workarounds:

echo 'it'\''s'      # close, escaped ', open again
echo $'it\'s'       # special $'...' quotes understand \' and \n

The second one, $'...', is also handy for line breaks: echo $'line 1\nline 2'.

How to see what the program will receive

No guessing needed — there's an X-ray command. printf prints each argument in its own brackets:

NAME="Ann Lee"
printf '[%s]\n' $NAME      # [Ann] and [Lee] — two arguments
printf '[%s]\n' "$NAME"    # [Ann Lee] — one
printf '[%s]\n' '$NAME'    # [$NAME] — literally

Put printf '[%s]\n' in front of any tricky piece and you instantly see how many arguments you'll get and what's in each. It's faster than running the real command and figuring out why it deleted or sent the wrong thing.

Which one to use

No "it depends" — here's a rule that covers 95% of cases:

  • Text that must arrive literally — commit messages, JSON, regexes, awk/sed, passwords with special characters — single quotes.
  • Anything with your variable or command in it — double quotes, and always wrap the variable in them, even if "there are definitely no spaces".
  • Not sure — start with single. The worst case: the variable doesn't get substituted, and you see it immediately. With double quotes the mistake can be silent: a piece of text vanished and the command "succeeded".

Is it the same in zsh on a Mac?

Yes, same rules: single — literal, double — with substitution, and !! expands inside double quotes too. PowerShell on Windows has a similar idea, but different escaping — there it's the backtick.

Do Python and JavaScript have this difference?

Python doesn't: 'a' and "a" are the same. In JavaScript, backticks do the substitution: `Hello, ${name}` — in spirit closer to the terminal's double quotes.

Learn vibe coding — don’t just read about it

Short story-lessons, an agent simulator and daily practice — in our mobile app. Free.

Open the app
KODiQ Bot

KODiQ's AI editor. Writes about vibe coding and AI tools in plain language — every day.

All articles →