Tools

jq: process JSON in the terminal with ten essential expressions

TL;DR

jq reads, filters and transforms JSON in the terminal and pairs naturally with curl. Learn ., .[], select, map, group_by and @csv; use -r for shell variables, --arg for inputs, and never overwrite the source file in place.

APIs return JSON, config files are JSON, logs are JSON. Every time they need one field or a few filtered items, many people open a browser, paste into an online tool, or write a quick Python script. jq does it right in the terminal: a compact query language for reading, filtering and transforming JSON that pairs naturally with curl and other commands. About ten expressions cover most daily needs.

GitHub page of jqlang/jq
GitHub page of jqlang/jq (screenshot, November 2026)

GitHub: https://github.com/jqlang/jq · about 36k stars · C · MIT

Installation

brew install jq          # macOS
sudo apt install jq      # Ubuntu/Debian
# Or via Docker, per the README:
docker run --rm -i ghcr.io/jqlang/jq:latest < package.json '.version'

The ten most-used expressions

Examples use users.json, an array of objects {"id", "name", "age", "team"}:

ExpressionMeaning
.Pretty-print the JSON with colours
.[0].nameThe name field of the first element
.[] | .nameIterate the array, take each name
map(.id)New array containing only ids
.[] | select(.age > 30)Filter by a condition
{name, team}Build a new object with just those fields
lengthCount elements
group_by(.team) | map({team: .[0].team, n: length})Group and count by team
sort_by(.age) | reverseSort descending
.[] | [.id, .name] | @csvExport CSV

Real-world examples

# Names and star counts of an organisation's GitHub repos
curl -s "https://api.github.com/orgs/astral-sh/repos?per_page=100" \
  | jq -r '.[] | "\(.stargazers_count)\t\(.name)"' | sort -rn | head

# Read the version from package.json in a script
VERSION=$(jq -r .version package.json)

# Pass shell variables into jq safely
jq --arg env "prod" '.[] | select(.env == $env)' deployments.json

# Change a field and write to a new file
jq '.scripts.test = "vitest run"' package.json > tmp.json && mv tmp.json package.json

# Filter JSON logs by level
cat app.log | jq -c 'select(.level == "error") | {time, msg}'

Important flags

  • -r: print strings without quotes, needed when assigning to shell variables.
  • -c: one result per line, compact for further piping.
  • -s: slurp multiple JSON inputs into one array (useful for JSON-lines logs).
  • -n: don't read input; build new JSON.
  • -e: exit non-zero if the result is null/false, handy in script conditions.

Caveats

  • Never do jq ... file > file: the shell truncates the file before jq reads it. Write to a temp file and rename.
  • Use --arg instead of splicing shell strings into jq expressions to avoid quoting bugs and injection.
  • jq loads whole documents into memory; for very large JSON consider --stream.

How to apply it

  • Dev: Use jq to explore API responses while debugging, and in build scripts to read versions and config.
  • Tech Lead: Standardise on JSON logs so the whole team can query them with jq during incidents, without heavy tooling.
  • PM: A single jq line can answer a quick data question from an API (how many records, grouped by status) without waiting for a dashboard.

Sources & references

  1. jqlang/jq — jqlang (GitHub)