Bash has no built-in JSON support, and trying to parse JSON with grep, sed, or awk is fragile and breaks the moment formatting changes. The right tool is jq. This guide shows how to parse JSON in shell scripts reliably.
Why not grep or sed?
JSON is a structured format with nesting, escaping, and flexible whitespace. Regex-based tools cannot reliably understand that structure — a script that works today breaks when the API minifies its output or reorders fields tomorrow. Use a real JSON parser.
Install jq
# macOS
brew install jq
# Ubuntu / Debian
sudo apt install jqExtract a single field
echo '{"name": "Alice", "age": 30}' | jq '.name'
# "Alice"
# Use -r for raw output (no quotes)
echo '{"name": "Alice"}' | jq -r '.name'
# AliceAssign a JSON value to a shell variable
This is the most common need. Always use -r so you do not get quotes in your variable:
NAME=$(echo "$JSON" | jq -r '.name')
TOKEN=$(curl -s https://api.example.com/auth | jq -r '.token')
echo "Hello, $NAME"Parse a file
jq -r '.database.host' config.json
# Or read it in
HOST=$(jq -r '.database.host' config.json)Validate your JSON before scripting
Paste your JSON to confirm it is valid and explore its structure — so your jq paths are correct the first time. Free and private.
Loop over a JSON array
The safe pattern uses -c to emit one compact object per line, then reads them in a while loop:
echo "$JSON" | jq -c '.users[]' | while read -r user; do
name=$(echo "$user" | jq -r '.name')
echo "User: $name"
doneTo loop over just one field:
jq -r '.users[].email' data.json | while read -r email; do
echo "Sending to $email"
doneFilter inside a script
# Only active users
jq -r '.users[] | select(.active == true) | .name' data.json
# Count array elements
COUNT=$(jq '.items | length' data.json)
echo "Found $COUNT items"Handle API responses safely
response=$(curl -s -w "%{http_code}" https://api.example.com/data)
http_code="${response: -3}"
body="${response%???}"
if [ "$http_code" -eq 200 ]; then
echo "$body" | jq -r '.result'
else
echo "Request failed: $http_code"
fiCheck if a field exists
if echo "$JSON" | jq -e '.error' > /dev/null; then
echo "Response contains an error"
fiThe -e flag sets the exit code based on the result, which is perfect for if statements.
Common mistakes
- Forgetting
-r: your variables end up wrapped in quotes. - Parsing with grep: works until the JSON formatting changes — then it silently breaks.
- Not quoting variables: always use
"$VAR"to handle spaces and special characters.