Cheat sheet · No. XII
jq.
jq is a pipeline language for JSON: every filter takes a value and emits zero or more values, and | feeds one filter into the next — exactly like the shell it lives in.
The reference
SELECTORS
.- The whole input (pretty-prints)
.name- Field
.user.address.city- Nested path
.[0]- Array index
.[]- Iterate — one output per element
.field?- No error if missing
.["odd key"]- Keys that aren't identifiers
PIPES & FILTERS
a | b- Feed a's output into b
select(.age > 21)- Keep or drop a value
map(.price)- Rewrite each array element
map(select(.active))- Filter an array in place
keyslength- Object keys / size of anything
has("id")- Key present?
..- Recursive descent — every value
RESHAPE
sort_by(.date)- Sort an array
group_by(.status)- Array of arrays, grouped
unique_by(.id)- Dedup on a key
min_by(.x)max_by(.x)- Extremes
add- Sum numbers / merge objects
to_entries- Object → [{key, value}] pairs
{id, name}- Pick fields into a new object
STRINGS & OUTPUT
"\(.first) \(.last)"- String interpolation
join(", ")split(",")- Array ↔ string
tostringtonumber- Casts
@csv@tsv- Array of scalars → CSV/TSV row
@json- Value → JSON string
@base64@base64d- Encode / decode
FLAGS
-r- Raw strings, no quotes — for shell pipelines
-c- Compact, one JSON value per line
-s- Slurp: wrap all inputs into one array
-n- No input — generate from scratch
--arg k v- Pass a shell value in as $k (safely)
-e- Exit 1 on null/false — for scripts
-S- Sort object keys (stable diffs)
ONE-LINERS
jq -r '.items[].name' f.json- Extract one field per line
jq '[.[] | select(.active)]' f.json- Filter an array, keep it an array
jq -r '.[] | [.id, .name] | @csv' f.json- JSON → CSV
jq 'group_by(.status) | map({(.[0].status): length})' f.json- Count by field
curl -s api.example.com/users | jq '.[0]'- Peek at the first record
Field notes
Reach for -r in pipelines
By default jq prints JSON, quotes included. -r emits raw strings, which is what xargs, while read, and every other shell tool expects.
select filters, map transforms
select(cond) passes a value through or drops it; map(f) rewrites each element. Most one-liners are map(select(…)): keep some, reshape what survives.
Pass shell values with --arg
jq --arg u "$USER" '.[] | select(.owner == $u)' avoids quoting bugs and injection. Never splice shell variables into the filter text itself.
.[] fans out
A filter containing .[] emits one result per element, not an array. Wrap the whole expression in [ … ] when you need an array back.