Easy Cheat Sheet for Using jq on the Command Line

If my sed cheat sheet was for text, this one is for JSON. jq is the tool I reach for whenever an API, a kubectl command, or a config file hands me more JSON than I want to read.

All examples use this file, data.json:

{"users":[
  {"name":"Ada","role":"admin","tags":["a","b"]},
  {"name":"Linus","role":"dev","tags":[]}
]}

Pretty print and pick a field

jq . data.json
jq '.users[].name' data.json

The first prints the whole document with indentation. The second walks every item in users and prints each name. Add -r to drop the quotes around strings.

Filter with select

jq '.users[] | select(.role == "admin")' data.json

select keeps only the items where the condition is true. Pipes (|) work like they do in the shell: the output of the left side feeds the right side.

Build a new array or object

jq '[.users[].name]' data.json
jq '.users | map({name, role})' data.json

Wrapping an expression in [ ] collects the results into an array. map applies an expression to every element, and {name, role} is shorthand for {name: .name, role: .role}.

Count, join, and group

jq '.users | length' data.json
jq -r '.users | map(.name) | join(", ")' data.json
jq '.users | group_by(.role) | map({role: .[0].role, count: length})' data.json

Defaults for missing values

jq '.users[0].email // "none"' data.json

The // operator returns the right side when the left is null or missing.

Output for other tools

jq -r '.users[] | [.name, .role] | @csv' data.json

@csv, @tsv, and @sh format arrays for spreadsheets and shell scripts. Pair them with -r so you don’t get escaped quotes.

Update values

jq '.users |= map(.name |= ascii_upcase)' data.json

|= updates a value in place and returns the whole document, so you can redirect it to a new file.

Pass shell variables safely

jq -n --arg name "Ada" '{name: $name}'

Use --arg instead of string-interpolating shell variables into the filter. It handles quoting for you.

A real-world one-liner

kubectl get pods -o json | jq -r '.items[] | select(.status.phase != "Running") | .metadata.name'

That lists every pod that isn’t running. Once the pattern clicks, you’ll start piping everything into jq.