Command-line JSON processor. Reads JSON, applies a filter, writes JSON.

jq '.'         file.json       # pretty-print
echo '{}' | jq '.'
curl -s api/x | jq '.items[].name'

Invocation flags

FlagEffect
-rraw output (strings without quotes — pipe to xargs)
-Rraw input (each line as a string)
-sslurp — read all input into a single array
-ccompact output (one line per value)
-nnull input — start from null (build from scratch)
-eexit 1 if last output is null/false
-aASCII output
--arg k vbind $k to the string v
--argjson k vbind $k to the parsed-JSON v
--slurpfile k filebind $k to the parsed contents of file
-f file.jqread filter from a file

Identity, access, slicing

.                              # input as-is
.foo                           # field
.foo.bar                       # nested field
.["foo bar"]                   # bracket form (for weird keys)
.foo?                          # null instead of error if missing
.[0]                           # array index
.[-1]                          # last element
.[2:5]                         # slice (indices 2..4)
.[]                            # iterate: emit each element
.foo[]                         # all array elements under .foo
keys                           # sorted list of keys
keys_unsorted
values
length                         # array length / string length / object key count
has("foo")
in({"a":1})                    # for strings: is this key in given object?

Pipes, commas, parens

.a | .b                        # pipe — output of left feeds right
.a, .b                         # comma — emit BOTH values (two outputs)
(.a + .b) | tostring           # parens group

Constructing values

{ name: .user.name, id: .user.id }                # object literal
{ name, id }                                      # shorthand: { name: .name, id: .id }
{ (.k): .v }                                      # computed key
[.a, .b, .c]                                      # array literal
[.items[].name]                                   # collect iterator into array

Operators

.a + .b         # add (numbers), concat (strings/arrays), merge (objects, right wins)
.a - .b         # subtract (numbers), array difference
.a * .b         # multiply; for objects: deep merge
.a / .b         # divide; for strings: split
.a % .b         # modulo
== != < <= > >= and or not
//              # alternative — left if not null/false, else right:  .x // "default"

Selection and filtering

.[] | select(.age >= 18)
.[] | select(.tags | index("urgent"))
.[] | select(.name | test("^A"; "i"))             # regex match
map(select(.active))                              # keep matching elements
.items | map(.price) | add                        # sum of prices
group_by(.category)                               # array of arrays, grouped
unique_by(.id)
sort_by(.created_at) | reverse
min_by(.score)  max_by(.score)

Transformation

map(.name)                              # = [.[] | .name]
map(. + {tagged: true})                 # add a field to every item
to_entries                              # {"a":1} → [{"key":"a","value":1}]
from_entries                            # inverse
with_entries(.value |= (. * 2))         # transform each value
paths                                   # all paths to leaf values
paths(type == "number")                 # paths whose leaf is a number
leaf_paths
getpath(["a","b"])
setpath(["a","b"]; 42)
delpaths([["a","b"]])

Strings

.name | ascii_downcase
.name | ascii_upcase
.body | ltrimstr("Re: ")
.body | rtrimstr(".txt")
.text | split(",")
["a","b","c"] | join(",")
.s   | test("^foo"; "i")                # regex test (PCRE-like)
.s   | match("(\\d+)").captures         # named/unnamed regex captures
.s   | scan("\\w+")                     # all matches
.s   | sub("foo"; "bar")                # first match
.s   | gsub("foo"; "bar")               # all matches
@json  @csv  @tsv  @sh  @uri  @base64  @base64d   # formatters: "\(.x) is \(.y)" | @csv

Update assignment

.name   = "X"               # set
.name  |= ascii_upcase      # update through pipe (read-modify-write)
.tags  += ["new"]           # append
.score //= 0                # set if null/false (default)
del(.password, .secret)     # delete

Variables, functions, conditionals

.items[] | .price as $p | $p * 1.2
 
if .age >= 18 then "adult" else "minor" end
 
if   .score > 90 then "A"
elif .score > 80 then "B"
elif .score > 70 then "C"
else "F" end
 
# Define a function
def double: . * 2;
def discount($pct): . - (. * $pct / 100);
.price | discount(20)
 
# try/catch
try .may_fail catch "default"

Recursion

..                          # recursive descent — emit every subvalue
.. | numbers                # every number anywhere
.. | objects | select(has("id"))
recurse(.children[]?)       # walk a tree via .children

Reducing and folding

reduce .[] as $x (0; . + $x.amount)           # sum
foreach .[] as $x (0; . + $x.amount; .)       # running totals

Cookbook

# Pretty-print a stream of JSON objects (one per line)
kubectl get pods -o json | jq '.items[].metadata.name'
 
# Convert array of objects → CSV (header + rows)
jq -r '(.[0]|keys_unsorted) as $h | $h, (.[] | [.[$h[]]]) | @csv' data.json
 
# Read multiple files into one array
jq -s '.' a.json b.json
 
# Inline edit (jq has no -i — use a tempfile)
tmp=$(mktemp) && jq '.version = "1.2.3"' pkg.json > "$tmp" && mv "$tmp" pkg.json
 
# Top-N
jq 'sort_by(.score) | reverse | .[0:5]' items.json
 
# Flatten nested arrays one level
jq '[.[][]]' nested.json
 
# Diff-friendly key sort
jq -S '.' a.json > a.sorted.json