jq

Quick reference for filtering, transforming, and querying JSON on the command line. updated Mar 30, 2026

Basics

# Pretty print
echo '{"a":1}' | jq .

# Read from file
jq . data.json

# Raw output (no quotes)
jq -r '.name' data.json

# Compact output
jq -c . data.json

# Exit status (for scripting)
jq -e '.enabled' data.json    # exit 1 if result is null/false

Selecting Fields

# Single field
jq '.name'

# Nested field
jq '.user.address.city'

# Multiple fields
jq '{name: .name, age: .age}'

# Optional field (no error if missing)
jq '.maybe?'
jq '.user?.address?.city'

Arrays

# All elements
jq '.[]'

# Specific index
jq '.[0]'
jq '.[-1]'                    # last element

# Slice
jq '.[2:5]'
jq '.[:3]'                    # first 3

# Length
jq '. | length'

# Array of a field from objects
jq '.[].name'
jq '[.[].name]'               # keep as array

# Flatten
jq 'flatten'
jq 'flatten(1)'               # one level

Filtering

# Select by condition
jq '.[] | select(.age > 30)'
jq '.[] | select(.status == "active")'
jq '.[] | select(.name | startswith("J"))'
jq '.[] | select(.tags | contains(["prod"]))'

# Multiple conditions
jq '.[] | select(.age > 20 and .age < 40)'
jq '.[] | select(.role == "admin" or .role == "owner")'

# Null check
jq '.[] | select(.email != null)'
jq '.[] | select(.email // empty)'

# Has key
jq '.[] | select(has("email"))'

# Type check
jq '.[] | select(type == "string")'

Transforming

# Map over array
jq 'map(.name)'
jq 'map({name, upper_name: (.name | ascii_upcase)})'
jq 'map(select(.active))'

# Add field
jq '.[] | . + {fullName: (.first + " " + .last)}'

# Remove field
jq 'del(.password)'
jq 'map(del(.internal_id))'

# Update field
jq '.name = "new name"'
jq '.count += 1'
jq '(.items[] | select(.id == 3)).status = "done"'

# To/from entries (object <-> key-value pairs)
jq 'to_entries'               # {a:1} -> [{key:"a",value:1}]
jq 'from_entries'             # reverse
jq 'to_entries | map(.value += 1) | from_entries'
jq 'with_entries(select(.value > 0))'

String Operations

# Split / join
jq '.csv | split(",")'
jq '.tags | join(", ")'

# Test (regex match)
jq 'select(.name | test("^[Jj]"))'

# Capture (regex groups)
jq '.version | capture("(?<major>\\d+)\\.(?<minor>\\d+)")'

# Replace
jq '.name | gsub("old"; "new")'
jq '.path | sub("/api/v1"; "/api/v2")'

# String interpolation
jq '"Hello, \(.name)! Age: \(.age)"'

# Length, ltrimstr, rtrimstr
jq '.name | length'
jq '.path | ltrimstr("/")'
jq '.file | rtrimstr(".json")'

# ascii_downcase / ascii_upcase
jq '.name | ascii_downcase'

Aggregation

# Count
jq '. | length'
jq '[.[] | select(.active)] | length'

# Sum
jq '[.[].price] | add'
jq 'map(.amount) | add'

# Min / max
jq 'min_by(.age)'
jq 'max_by(.score)'
jq '[.[].price] | min'

# Sort
jq 'sort_by(.name)'
jq 'sort_by(.date) | reverse'

# Unique
jq '[.[].category] | unique'
jq 'unique_by(.email)'

# Group
jq 'group_by(.category)'
jq 'group_by(.status) | map({status: .[0].status, count: length})'

# First / last
jq 'first(.[] | select(.active))'
jq 'last'

Object Construction

# Build new object
jq '{id: .user_id, name: .user_name}'

# Collect into array
jq '[.[] | {name, status}]'

# Merge objects
jq '. * {"new_field": "value"}'
jq '.[0] * .[1]'                     # merge two objects

# Reduce
jq 'reduce .[] as $item (0; . + $item.count)'
jq 'reduce .[] as $item ({}; . + {($item.key): $item.value})'

Variables and Functions

# Assign variable
jq '.users as $u | .orders | map(. + {user: ($u[] | select(.id == .user_id))})'

# Pass variable from shell
jq --arg name "$NAME" '.[] | select(.name == $name)'
jq --argjson count "$COUNT" '.limit = $count'

# Slurp (read all inputs as array)
jq -s '.' file1.json file2.json
jq -s 'map(.name) | unique' *.json

# Null input (construct from nothing)
jq -n '{name: "new", count: 0}'
jq -n --arg name "$NAME" '{name: $name}'

# Raw input (lines to JSON)
jq -R . <<< "hello"               # "hello"
jq -Rn '[inputs]' file.txt        # lines to array

Conditionals

# if-then-else
jq 'if .age >= 18 then "adult" else "minor" end'

# Alternative operator (default)
jq '.name // "unknown"'
jq '(.config.timeout // 30)'

# try-catch
jq 'try .foo.bar catch "missing"'
jq '[.[] | try tonumber]'

Useful Patterns

# kubectl + jq
kubectl get pods -o json | jq '.items[] | {name: .metadata.name, status: .status.phase}'
kubectl get nodes -o json | jq '.items[] | {name: .metadata.name, cpu: .status.capacity.cpu}'

# Terraform + jq
terraform show -json | jq '.values.root_module.resources[] | {type, name: .name, id: .values.id}'

# AWS CLI + jq
aws ec2 describe-instances | jq '.Reservations[].Instances[] | {id: .InstanceId, state: .State.Name, type: .InstanceType}'

# Flatten nested API response
jq '{items: [.data.results[] | {id, title: .name, active: (.status == "active")}]}'

# CSV-ish output
jq -r '.[] | [.name, .email, .role] | @csv'

# TSV output
jq -r '.[] | [.name, .email] | @tsv'

# Convert array of objects to lookup map
jq 'map({(.id | tostring): .}) | add'

# Diff two JSON files
diff <(jq -S . a.json) <(jq -S . b.json)