Overview

jq is a filter language for JSON on the command line. Examples use jq 1.7 and this input unless stated:

[{"name":"alice","age":30,"tags":["admin","user"]},
 {"name":"bob","age":25,"tags":["user"]},
 {"name":"carol","age":35,"tags":["admin"]}]

For JSON inside Postgres see postgres-jsonb; for fetching the JSON see curl-flags.

Select

Select with .[] and select(), and wrap in [...] when you need an array.

FilterResult
.[0], .[-1], .[1:3]First, last, slice.
.[].name"alice", "bob", "carol" as separate outputs.
[.[].name]One array of names.
.["user-name"]Key with special characters.
.foo?Suppress errors when .foo is the wrong type.
..Every value, recursively.
keys, length, type, has("name")Introspection.
.[] | select(.age > 28)Keep matching elements.
map(select(.tags | contains(["admin"])))Users with the admin tag.
first(.[] | select(.age > 28))Stop at the first match.
any(.[]; .age > 33), all(.[]; .age > 20)Boolean tests.

Transform

Prefer map and to_entries over manual loops.

FilterResult
{n: .name, a: .age}Build an object.
map({name}), map(pick(.name, .age))Keep chosen fields (pick needs 1.7).
map(del(.tags))Remove a field.
map(. + {senior: (.age > 30)})Add a field.
map(.tags) | flatten, flatten(1)Flatten fully, or one level.
map(.age |= . + 1)Update in place.
sort_by(.age), sort_by(.age) | reverseAscending, descending.
group_by(.age), unique_by(.name)Group, deduplicate.
min_by(.age), max_by(.age)Extremes.
[.[].age] | add / lengthAverage.
map(.tags[]) | uniqueDistinct tags.
to_entries, from_entries, with_entries(f)Object to key/value pairs and back.
reduce .[] as $u (0; . + $u.age)Fold.
[limit(2; .[])]First two outputs.
map(.name | ascii_upcase), test("^a"), sub("a";"A")String and regex helpers.
walk(f), paths, getpath(["a","b"])Walk nested data.

Control flow

Use // for defaults and try only when an error is expected.

FilterResult
.x // "default"Right side when .x is null, false, or empty.
if .age > 30 then "senior" else "junior" endConditional.
try .x catch "missing"Recover from an error.
if .type == "user" then .email else empty endempty emits nothing.
. as $in | ...Bind the input to a variable.

Formats

Combine with -r so the output is not JSON-quoted.

FilterOutput
@csv, @tsv[.name,.age] | @csv gives "alice",30.
@uri, @html, @shURL-encode, HTML-escape, shell-quote a string.
@base64, @base64dEncode, decode.
tojson, fromjsonValue to JSON string, and back.

CLI flags

Use -r when output feeds a shell variable and -e when it feeds a test.

FlagEffect
-r / -jRaw strings, with / without trailing newline.
-c / -S / --tabCompact / sorted keys / tab indent.
-eExit status 1 when the last output is null or false.
-nNo input; use with inputs or --arg.
-sSlurp all inputs into one array.
--arg n v / --argjson n jsonPass a string / a JSON value as $n.
--slurpfile n fLoad a file’s JSON values into $n.

jq has no in-place flag. Write to a temp file and move it, or use sponge:

jq '.version = "2.0"' pkg.json > tmp && mv tmp pkg.json
jq -s '.[0] * .[1]' base.json patch.json          # deep-merge two files
jq -s add part1.json part2.json                   # concatenate arrays from several files
jq -r '.[] | [.name, .age] | @csv' users.json     # CSV rows
jq --arg n "$NAME" '.[] | select(.name == $n)'    # shell value in a filter

Gotchas

Avoid these; each yields null or several outputs where you expected one value.

GotchaFix
A missing key yields null, not an error.Use has("k") to tell missing from null.
// also fires on false and on empty output, but not on 0 or "".Test explicitly for those values.
.[].x emits many values, and .a,.b is two outputs.Wrap in [...] for an array.
-r only unquotes strings; arrays still print as JSON.jq -r '.[]' for one item per line.
Number literals over 2^53 survive untouched in jq 1.7+, but arithmetic uses doubles.Keep big IDs as strings.
sort_by on mixed types orders by type, not value.Normalize with tostring or tonumber first.
Comments start with #; // is the alternative operator.Do not use // as a comment.