APIs, Docker, Kubernetes, cloud CLIs and many log formats speak JSON. jq is the command-line tool for reading, filtering and reshaping it, like sed and awk for JSON. A few filters cover most everyday tasks.

Install

sudo apt install jq          # Debian/Ubuntu
sudo dnf install jq          # RHEL/AlmaLinux

Pretty-print

curl -s https://api.github.com/repos/jqlang/jq | jq .
cat data.json | jq .
jq . data.json
jq -c . data.json            # compact, one line

Select fields

Given server.json:

{"name":"web1","ip":"203.0.113.10","tags":["nginx","prod"],"disk":{"total":80,"used":52}}
jq '.name' server.json             # "web1"
jq -r '.name' server.json          # web1  (raw, no quotes: use this in scripts)
jq '.disk.used' server.json        # 52
jq '.tags[0]' server.json          # "nginx"
jq '{name, ip}' server.json        # new object with only these fields
jq -r '"\(.name) \(.ip)"' server.json   # string interpolation: web1 203.0.113.10

Arrays: iterate, filter, map

Given servers.json, an array of objects like the one above:

jq '.[]' servers.json                                  # each element
jq -r '.[].name' servers.json                          # all names
jq '.[] | select(.disk.used > 70)' servers.json        # filter
jq -r '.[] | select(.tags | index("prod")) | .ip' servers.json
jq 'map({name, pct: (.disk.used / .disk.total * 100 | floor)})' servers.json
jq 'length' servers.json                               # count
jq 'sort_by(.disk.used) | reverse | .[0].name' servers.json
jq '[.[].disk.used] | add' servers.json               # sum

Use jq in shell scripts

TOKEN=$(curl -s -X POST https://api.example.com/login -d '{"u":"x","p":"y"}' -H 'Content-Type: application/json' | jq -r '.token')

for ip in $(jq -r '.[].ip' servers.json); do
  ping -c1 -W1 "$ip" >/dev/null && echo "$ip up" || echo "$ip down"
done

Use -e to make jq’s exit status reflect the result (non-zero on null/false), which is handy in if statements. Pass shell variables safely with --arg:

jq --arg host "web2" '.[] | select(.name == $host)' servers.json

Build JSON for API calls

jq -n --arg name "web3" --arg ip "203.0.113.12" '{name: $name, ip: $ip}' |
  curl -s -X POST https://api.example.com/servers -H 'Content-Type: application/json' -d @-

jq -n builds JSON safely (correct quoting and escaping), instead of fragile string concatenation.

Edit a JSON file

jq '.disk.total = 160' server.json > tmp.json && mv tmp.json server.json
jq '.tags += ["backup"]' server.json
jq 'del(.disk)' server.json

jq doesn’t edit files in place. Write to a temporary file, then move it (or use sponge from moreutils).

Server examples

docker inspect nginx | jq -r '.[0].NetworkSettings.Networks[].IPAddress'
docker inspect nginx | jq '.[0].Mounts'
kubectl get pods -o json | jq -r '.items[] | select(.status.phase != "Running") | .metadata.name'
# JSON logs (one object per line)
jq -r 'select(.level == "error") | "\(.time) \(.msg)"' app.log
cat access.json | jq -r '.remote_addr' | sort | uniq -c | sort -rn | head

See Docker container IP address and kubectl cheat sheet.

For YAML, use yq, which has jq-like syntax. See yq for YAML.

Troubleshooting

  • “parse error: Invalid numeric literal”: the input isn’t JSON. Often it’s an HTML error page from an API, or extra text before the JSON. Inspect it with curl -i.
  • Output in quotes in scripts: add -r for raw strings.
  • null results: the path doesn’t exist, or the key name differs in case. Check with jq 'keys'.
  • Large files: jq loads the whole document into memory. For huge JSON-lines logs, process one line at a time (the default for newline-delimited input), or use --stream.

Quick reference

Task Filter
Pretty-print jq .
Field (raw) jq -r '.field'
Each array element .[]
Filter select(.x > 1)
Transform array map({a, b})
Count / sum length, add
Shell variable --arg name "$var" then $name
Build JSON jq -n '{…}'

Related: curl command examples and pipes and redirection.

  • Linux
  • JSON
  • Command Line