Blog
Wild & Free Tools

Escape JSON for Bash Shell Scripts — Practical Patterns

Last updated: April 2026 6 min read
Quick Answer

Table of Contents

  1. Single-Quoted Literal JSON
  2. Heredoc for Multi-Line JSON
  3. Build JSON With jq
  4. Reading JSON Out of curl Responses
  5. When to Stop Fighting the Shell
  6. Frequently Asked Questions

Embedding JSON inside a bash script is one of those problems that looks simple until you have a string with a single quote, an apostrophe, or a variable that needs to interpolate. Three patterns cover almost every case: single-quoted literals, heredocs, and jq for dynamic generation.

The Single-Quote Pattern (Static JSON)

For JSON that doesn't change between runs, single-quote the whole thing:

BODY='{"name": "Alex", "active": true}'
curl -X POST -d "$BODY" https://api.example.com/users

Bash treats everything inside single quotes as literal. Double quotes, backslashes, dollar signs — all preserved exactly. The only problem character is the single quote itself, which can't appear inside single-quoted text.

If your JSON has single quotes, the workaround is the famous '\'' sequence:

BODY='{"name": "O'\''Brien"}'

That's: close the single-quoted string, escape a literal single quote, reopen the single-quoted string. Ugly, but standard.

Heredocs for Multi-Line JSON

For longer or multi-line JSON, a heredoc avoids quoting entirely:

BODY=$(cat <<'EOF'
{
  "name": "Alex",
  "bio": "She said \"hi\".",
  "tags": ["a", "b"]
}
EOF
)
curl -d "$BODY" https://api.example.com/users

The single quotes around the opening 'EOF' matter — they tell bash NOT to interpolate variables or expand backticks inside the heredoc. Without them, every $ in your JSON gets eaten.

Use double-quoted EOF (or no quotes) when you DO want variable interpolation:

USER_NAME="Alex"
BODY=$(cat <<EOF
{ "name": "$USER_NAME" }
EOF
)

Variables expand, but you've also lost protection from accidental shell expansion of any $ in the JSON.

Sell Custom Apparel — We Handle Printing & Free Shipping

Build JSON Dynamically With jq

For JSON that mixes shell variables and literal structure, hand-escaping gets fragile fast. jq solves this:

NAME="O'Brien"
EMAIL="[email protected]"

BODY=$(jq -n \
  --arg name "$NAME" \
  --arg email "$EMAIL" \
  '{name: $name, email: $email}')

# BODY is now valid JSON: {"name":"O'Brien","email":"[email protected]"}
curl -d "$BODY" https://api.example.com/users

jq handles all the escaping. Your variables can contain quotes, backslashes, newlines — anything — and jq produces valid JSON. This is the right pattern for any script that builds JSON from user input or dynamic values.

For arrays of values, use --argjson to pass already-JSON values, or build the array structure in the jq filter.

Reading JSON Back Out of curl Responses

The reverse direction — extracting values from a JSON response — also benefits from jq:

RESPONSE=$(curl -s https://api.example.com/users/1)
NAME=$(echo "$RESPONSE" | jq -r '.name')
echo "User name: $NAME"

The -r flag (raw) strips the outer quotes from string values. Without it, $NAME would be "Alex" with literal quotes; with it, $NAME is just Alex.

For nested values: jq -r '.user.profile.email'. For array iteration: jq -r '.users[].name'. For tabular extraction: jq -r '.users[] | [.id, .name] | @tsv'.

jq is worth learning if you do any meaningful shell scripting against APIs. The syntax is its own thing but it pays for itself within a few scripts.

When to Stop Fighting the Shell

If your script has more than ~10 lines of JSON manipulation, the shell is probably the wrong tool. Three signs to switch:

Move to Python, Node, or any real language with proper JSON support. Bash is great for orchestration; it's terrible for serious data manipulation. The line where it stops being worth it is somewhere around "I need to merge two JSON objects."

Escape JSON for Bash

Paste your JSON, get a shell-safe escaped version. Free, browser-based.

Open Free JSON Escape / Unescape Tool

Frequently Asked Questions

Why does my bash variable break the JSON?

Usually unquoted expansion. Always wrap variables in double quotes when used inside JSON: "$VAR" not $VAR.

How do I escape a single quote in a single-quoted bash string?

Use the sequence '\\'\' — close, escape, reopen. Or use a heredoc with no quotes.

Can I use jq to escape arbitrary text for JSON?

Yes: echo "your text" | jq -Rs . produces a properly escaped JSON string value.

What about Windows Subsystem for Linux?

Same bash rules apply inside WSL. Pure Windows cmd or PowerShell uses different quoting — see the curl PowerShell guide.

Andrew Walsh
Andrew Walsh Developer Tools & API Writer

Andrew worked as a developer advocate at two SaaS startups writing API documentation used by thousands of engineers.

More articles by Andrew →
Launch Your Own Clothing Brand — No Inventory, No Risk