Home / Alt manpages / jq(1)

  • jq(1)
  • User command
  • linux

Extract, Reshape and Validate JSON with jq

A 2,000-line JSON blob and you need one field: jq gets it in a single line. Learn to select records, reshape them and make a script stop when the data is missing. Allow about fifteen minutes.

  • You need: a shell, jq, and a JSON file or a command that produces JSON.
  • Read-only: nothing here changes a file unless you deliberately redirect output over an existing one.
  • Tested on: jq 1.7.1, the version installed on this machine. Its manpage is the local contract for these examples.

1. Check the installed version

Check the executable before relying on an option:

$ jq --version
jq-1.7

Yes, that says 1.7. The package is 1.7.1-3ubuntu0.24.04.2, but the program reports jq-1.7. Keep that distinction in bug reports and deployment notes.

2. Pretty-print and inspect JSON

Start with the identity filter: a single full stop. jq reads a stream of JSON values and pretty-prints each result by default.

$ printf '%s\n' '{"user":{"name":"Ada","roles":["admin","reviewer"]}}' | jq '.'
{
  "user": {
    "name": "Ada",
    "roles": [
      "admin",
      "reviewer"
    ]
  }
}

Tip

Always wrap the filter in single quotes so the shell leaves jq's punctuation alone.

Checkpoint

Formatted JSON means the executable and input parsing both work. A parse error means you should inspect the producer first; do not hide malformed input behind a cleverer filter.

3. Select fields and array items

  • Dot notation for identifier-like keys: .name.
  • Bracket notation for keys containing punctuation.
  • Array indexes start at zero.
  • .[] iterates, emitting one result per item.
$ printf '%s\n' '[{"name":"Ada","active":true},{"name":"Grace","active":false}]' \
    | jq '.[] | select(.active) | .name'
"Ada"

select keeps its input only when the condition is true. The pipe feeds each result on the left into the filter on the right.

For a plain shell string instead of a quoted JSON string, add -r:

$ printf '%s\n' '{"name":"Ada"}' | jq -r '.name'
Ada

Tip

Raw output is a presentation choice, not validation. Keep JSON output when the next command also expects JSON.

4. Reshape records into a compact result

Object construction gives you a stable output shape. This builds a new object for each active record, and -c puts the result on one line:

$ printf '%s\n' '[{"name":"Ada","role":"admin","active":true},{"name":"Grace","role":"reviewer","active":false}]' \
    | jq -c '[.[] | select(.active) | {name, role}]'
[{"name":"Ada","role":"admin"}]

Warning

The outer square brackets collect all results into one JSON array. Without them jq emits separate values, one per match, and a downstream program expecting one document may receive several valid documents instead. This is a common pipeline trap.

For a key whose spelling is not a jq identifier, quote it:

$ printf '%s\n' '{"user-name":"Ada"}' | jq -r '."user-name"'
Ada

5. Pass values in, do not paste them in

Keep data out of the filter text and you avoid fragile shell interpolation:

  • --arg binds a string.
  • --argjson binds a JSON value.
$ printf '%s\n' '[{"name":"Ada","active":true},{"name":"Grace","active":false}]' \
    | jq --arg wanted 'Ada' '[.[] | select(.name == $wanted)]'
[
  {
    "name": "Ada",
    "active": true
  }
]

$ jq -n --argjson limit 3 '$limit * 2'
6

Warning

--arg wanted 123 binds the string "123", not the number 123. Use the option that matches the type your filter expects.

6. Validate input and make scripts stop

Parsing with the identity filter is a handy read-only check:

$ jq -e '.' data.json >/dev/null
$ echo $?
0

With -e, the exit status tells you about the last result:

  • 0: the last result was neither false nor null.
  • 1: the last result was false or null.
  • 4: no valid result was produced.
  • Anything else: a usage, system or compilation failure.

That makes an explicit predicate useful in a script:

if jq -e 'any(.[]; .active == true)' data.json >/dev/null; then
    printf '%s\n' 'at least one active record'
else
    status=$?
    printf 'no active record or jq failed (status %s)\n' "$status" >&2
    exit "$status"
fi

Warning

Not every failure means nothing matched. A status such as 2 or 3 means the command or filter failed, so read stderr and check the filter before you touch the data.

7. Handle large inputs deliberately

jq normally parses one JSON value at a time. Two options change that:

  • -s slurps all input values into one big array. Handy for aggregation, but memory use grows with the input.
  • --stream emits path and leaf-value arrays from a large single document, so a filter can reduce it incrementally. It changes the input shape; it is not a drop-in speed switch for an existing filter.

Two formatting options for reviews and logs (they change formatting, not the selected values):

  • -S sorts object keys, for stable diffs and tests.
  • -M turns off terminal colour, for logs and snapshots.

8. Write output safely

Everything above writes to standard output only. When you need a file, redirect to a new name first and check it:

$ jq '.' data.json > data.pretty.json
$ jq -e '.' data.pretty.json >/dev/null

Checkpoint

The second command should exit 0. If the output is wrong, remove it with rm -- data.pretty.json or keep it for comparison; the original data.json is untouched.

Warning

Never write jq '.' data.json > data.json. The shell truncates the destination before jq can finish reading it, and there is no undo unless another copy exists.

Warning

Use sudo only when the input or destination genuinely needs elevated access; filtering a file in your working directory normally needs none. Do not give jq extra access just to silence a permissions error. Pick a permitted working directory or fix ownership through your normal administration process.

Done means

  • Version confirmed: jq --version reports the expected installed release.
  • Errors told apart: you can format input with jq '.' and distinguish malformed JSON from a filter error.
  • Records reshaped: you can select records, build a new object and choose between JSON and raw string output.
  • Types right: --arg supplies strings, --argjson supplies JSON values.
  • Scripts stop: your validation uses -e on purpose and checks its exit status.
  • Originals safe: any transformed file went to a new path and was checked before it replaced anything.