Where to start First steps track · 15 stages
Linux Labs

jq — processing JSON on the command line

4 min jq 1.7

Parsing and filtering JSON on the command line — fields, arrays, filters, formatting.

Cheat sheet

8 commands Printable version
  • jq . pretty-print
  • jq '.field' a field's value
  • jq '.a.b' a nested field's value
  • jq '.arr[]' every array element
  • jq -r '.[].name' raw output (for scripts)
  • jq '.[] | select(.x=="y")' a filter
  • jq -r '.[] | [.id,.name] | @tsv' a TSV table
  • jq --arg u "$USER" '.[]|select(.name==$u)' a shell variable into the filter

Overview

jq is like grep/sed, but for JSON. Indispensable when working with APIs (curl) and config files. It understands structure, not just text.

bash
echo '{"name":"web","port":80}' | jq .          # pretty-print
echo '{"name":"web","port":80}' | jq '.name'    # → "web"
curl -s https://api/x | jq '.items[0].id'       # a field from an API response

Options

8

-r strips the quotes from a string result — without it, a script gets the JSON literal "value" instead of value. --arg/--argjson are the only safe way to pass a shell variable into a filter: pasting a variable straight into the filter text breaks on quotes and special characters. -s (slurp) is needed when the input is a stream of several JSON objects that must be processed as a single array.

Option What it does Example
-r --raw-output Raw string output — without JSON quotes, convenient for scripts. jq -r '.name'
--arg name value Pass a shell variable's value into the filter as a string. jq --arg u "$USER" '.users[]|select(.name==$u)'
-c --compact-output Compact output — one JSON object per line, no indentation. jq -c '.'
-s --slurp Slurp the whole input stream into a single array before processing. jq -s '.'
--argjson name value Pass a value into the filter as JSON (number, object, array). jq --argjson n 5 '.items[:$n]'
-n --null-input Do not read input — useful for generating JSON from scratch. jq -n '{a:1}'
-e --exit-status Exit status 1 if the result is false or null; useful in scripts. jq -e '.ok'
-C --color-output Always colorize output, even when it is not going to a terminal. jq -C . file.json

Nothing found. Try a shorter query.

Recipes

2

Filters

bash
jq '.'                            # the whole JSON, pretty-printed
jq '.key'                         # a field
jq '.a.b.c'                       # a nested field
jq '.key // "default"'            # the value, or a default if null/missing
jq '.items[]'                     # every array element
jq '.items[0]'                    # the first element
jq '.items[-1]'                   # the last element
jq '.items | length'              # array length
jq 'keys'                         # the keys
jq 'has("name")'                  # does the key exist
jq '.items[] | .name'             # each element's name
jq '.items[] | select(.active)'   # filter by a condition
jq '.items[] | select(.cpu > 80)' # a numeric condition
jq '.items | map(.name)'          # an array of names
jq '.items | group_by(.role)'     # group them
jq -r '.name'                     # -r: "raw" output (no quotes) — for scripts

Examples and assembly

bash
# every service's IP from an API (as plain lines for a script)
curl -s https://api/services | jq -r '.[].ip'

# filter and build a table (TSV)
curl -s https://api/users | jq -r '.[] | select(.role=="admin") | [.id,.name] | @tsv'

# build a CSV with a header
jq -r '["id","name"], (.[] | [.id,.name]) | @csv' users.json

# substitute a shell variable into the filter (safely)
jq --arg u "$USER" '.users[] | select(.name==$u)' data.json

# build a new object
jq '{name: .name, port: .ports[0]}' svc.json

# edit a JSON file "in place" (sponge from moreutils)
jq '.version="2"' conf.json | sponge conf.json

Troubleshooting

4

jq: error: Cannot index ...

The structure isn't what you expect. Run jq . first (look at the shape), then build around it. Is the field an object or an array? .items[] for an array, .items.x for an object.

There are extra quotes in the output

jq prints strings with quotes. If you need raw text (for scripts/loops), use jq -r.

Invalid JSON on the input

jq fails on non-JSON. Check that the previous command actually produces JSON (not HTML or an error): curl -s URL | head. For a "stream" of JSON objects, use jq -s (slurp) or --stream.

You need to substitute a shell variable

Don't paste it into the filter text — use --arg name val (a string) or --argjson (a number/JSON), and reference it inside as $name.

See also

  • curl — HTTP/HTTPS requests from the CLI — debugging APIs and web services, headers, methods.
  • text-tools — Processing text and streams — sorting, uniqueness, columns, counting.
Loading…