| name | cli-jq |
| description | Covers effective use of jq for processing, filtering, and transforming JSON data. Activates when the user asks about jq, parsing JSON in the terminal, reshaping API responses, or transforming structured data with filters, pipes, select, map, or reduce.
|
jq — JSON Processor
Repo: https://github.com/jqlang/jq
Lightweight, powerful command-line JSON processor. Like sed for JSON: slice,
filter, map, and transform structured data with a compact expression language.
Reads JSON (or NDJSON), applies a filter, and outputs JSON (or plain text).
When to Activate
Manual triggers:
- "How do I use jq?"
- "Parse this JSON response"
- "Extract fields from JSON"
- "Filter/reshape an API response"
Auto-detect triggers:
- User is processing curl, gh, or httpie output and wants specific fields
- User has a JSON file and wants to extract or transform data
- User is scripting against an API and needs to reshape the response
- User wants to convert JSON to CSV or TSV
Key Commands
Running jq
jq '.' file.json
jq '.' <<< '{"a":1}'
curl -s url | jq '.'
jq -r '.name' file.json
jq -c '.'
jq -n '{a: 1}'
jq --arg name "Alice" '{name: $name}'
jq --argjson count 42 '{count: $count}'
jq -s '.'
jq -e '.exists' && echo "found"
Basic Filters
jq '.key'
jq '.a.b.c'
jq '.items[0]'
jq '.items[-1]'
jq '.items[2:5]'
jq '.items[]'
jq '.[] | .name'
jq 'keys'
jq 'values'
jq 'length'
jq 'has("key")'
jq 'in({"a":1})'
jq 'type'
Pipes and Construction
jq '.users[] | .email'
jq '.users[] | {name, email}'
jq '.users[] | {n: .name, e: .email}'
jq '[.users[] | .name]'
jq '{count: (.users | length)}'
String Operations
jq '.name | ascii_downcase'
jq '.name | ascii_upcase'
jq '.name | ltrimstr("prefix")'
jq '.name | rtrimstr("suffix")'
jq '.name | split(" ")'
jq '.parts | join(", ")'
jq '.name | test("^foo")'
jq '.name | match("(\\w+)@(\\w+)")'
jq '.name | gsub("foo"; "bar")'
jq '"Hello \(.name)!"'
jq '@uri'
jq '@html'
jq '@base64'
jq '@base64d'
select and Conditionals
jq '.[] | select(.age > 30)'
jq '.[] | select(.status == "active")'
jq '.[] | select(.name | startswith("A"))'
jq '.[] | select(.tags | contains(["go"]))'
jq '.[] | select(.score != null)'
jq 'if .age > 18 then "adult" else "minor" end'
jq '.value // "default"'
jq 'if . == null then empty else . end'
map and Arrays
jq 'map(.name)'
jq 'map(select(.active))'
jq 'map(. * 2)'
jq 'map({name: .name, upper: (.name | ascii_upcase)})'
jq '[.[] | .score] | add'
jq '[.[] | .score] | add / length'
jq 'min_by(.score)'
jq 'max_by(.score)'
jq 'sort_by(.name)'
jq 'sort_by(.created_at) | reverse'
jq 'unique_by(.email)'
jq 'group_by(.department)'
jq 'flatten'
jq 'flatten(1)'
jq 'first'
jq 'last'
jq 'nth(2)'
jq 'indices(",")'
jq 'any(.[]; . > 10)'
jq 'all(.[]; . > 0)'
reduce and Aggregation
jq 'reduce .[] as $x (0; . + $x)'
jq 'reduce .[] as $item ({}; . + {($item.id | tostring): $item})'
jq 'reduce .[] as $x ([]; if $x.active then . + [$x] else . end)'
jq 'reduce .[] as $x ({}; .[$x.status] += 1)'
jq 'group_by(.status) | map({status: .[0].status, count: length})'
Object Manipulation
jq '. + {"extra": "field"}'
jq 'del(.secret)'
jq 'del(.a, .b)'
jq 'with_entries(select(.value != null))'
jq 'with_entries(.value |= . * 2)'
jq 'with_entries(.key |= "prefix_" + .)'
jq 'to_entries'
jq 'from_entries'
jq 'to_entries | map(select(.value != "")) | from_entries'
Output Formats
jq -r '.[] | [.name, .age, .city] | @csv'
jq -r '.[] | [.name, .age] | @tsv'
jq -r '.[] | [.name, .score] | @tsv' | column -t
jq -c '.[]' large_array.json
jq -s '.' ndjson_file.json
Advanced Patterns
Reshaping API Responses
gh pr list --json number,title,author,labels \
| jq '.[] | {
pr: .number,
title,
author: .author.login,
labels: [.labels[].name]
}'
curl -s 'https://api.example.com/data' \
| jq '.data.items[] | {id, name: .metadata.name}'
jq -n \
--slurpfile users users.json \
--slurpfile scores scores.json \
'($users[0] | map({(.id|tostring): .}) | add) as $u |
$scores[0] | map(. + $u[(.userId|tostring)])'
Recursive Descent
jq '.. | .name? // empty'
jq '.. | numbers'
jq 'path(.. | .error?)'
jq '[leaf_paths]'
jq 'getpath(["a","b","c"])'
jq 'setpath(["a","b"]; 42)'
jq 'delpaths([["a","secret"]])'
Custom Functions
jq 'def log2: . as $n | 1 | until(. * 2 > $n; . * 2) | log / log(2) | floor;
.data[] | {value: ., log2: log2}'
jq 'def normalize($min; $max): (. - $min) / ($max - $min);
.scores[] | normalize(0; 100)'
jq 'def depth: if type == "object" or type == "array"
then [.[]] | map(depth) | max + 1
else 0 end;
depth'
try-catch
jq '.[] | try .value catch "parse error"'
jq 'try (.x / .y) catch "division error"'
jq '.items[] | try {name: .name, score: (.data | fromjson | .score)} catch {name: .name, score: null}'
NDJSON / Streaming Large Files
jq -c --stream 'if length == 2 and .[0][-1] == "name" then .[1] else empty end' huge.json
cat *.ndjson | jq -s 'map(select(.type == "event"))'
jq -cn --stream 'fromstream(1|truncate_stream(inputs; 1))' huge_array.json
Practical Examples
Extract and Summarize API Data
gh issue list --json labels --limit 500 \
| jq '[.[].labels[].name] | group_by(.) | map({label: .[0], count: length}) | sort_by(-.count)'
gh repo list myorg --json name,stargazerCount --limit 100 \
| jq 'sort_by(-.stargazerCount) | .[0:5] | .[] | "\(.stargazerCount)\t\(.name)"' -r
Transform Config Files
jq 'map(. + {env: "production"})' config.json
jq -s '.[0] * .[1]' base.json overrides.json
mapfile -t keys < <(jq -r 'keys[]' config.json)
Validate Data
jq '.[] | select((.name == null) or (.email == null)) | {id, missing: [if .name == null then "name" else empty end, if .email == null then "email" else empty end]}' records.json
Chaining with Other Skills
- gh (cli-gh):
gh ... --json | jq is the standard gh power combo — every gh resource supports --json output
- httpie (cli-httpie): HTTPie outputs JSON that pipes cleanly into jq; use
http GET url | jq '.field'
- yq (cli-yq): Convert YAML → JSON with
yq -o=json, then process with jq; or go the other way
- duckdb: Load NDJSON into DuckDB with
read_json_auto, or export query results as JSON and reshape with jq
- fzf (cli-fzf): Use jq to extract a field (e.g., PR numbers), then pipe into fzf for interactive selection