| name | jq |
| description | ALWAYS use this when the request matches JQ: Expert jq usage for JSON querying, filtering, transformation, and pipeline integration. |
jq — JSON Querying and Transformation
Selective Reading Rule
Start with:
references/senior-master-standard.md
references/usage-routing.md
references/quality-checklist.md
Then load only the inherited docs, scripts, assets, or examples that match the user's actual task.
Overview
jq is the standard CLI tool for querying and reshaping JSON. This skill covers practical, expert-level usage: filtering deeply nested data, transforming structures, aggregating values, and composing jq into shell pipelines. Every example is copy-paste ready for real workflows.
When to Use This Skill
- Use when parsing JSON output from APIs, CLI tools (AWS, GitHub, kubectl, docker), or log files
- Use when transforming JSON structure (rename keys, flatten arrays, group records)
- Use when the user needs
jq inside a bash script or one-liner
- Use when explaining what a complex
jq expression does
How It Works
jq takes a filter expression and applies it to JSON input. Filters compose with pipes (|), and jq handles arrays, objects, strings, numbers, booleans, and null natively.
Basic Selection
echo '{"name":"alice","age":30}' | jq '.name'
echo '{"user":{"email":"a@b.com"}}' | jq '.user.email'
echo '[10, 20, 30]' | jq '.[1]'
echo '[1,2,3,4,5]' | jq '.[2:4]'
echo '[{"id":1},{"id":2}]' | jq '.[]'
Filtering with select
echo '[{"role":"admin"},{"role":"user"},{"role":"admin"}]' \
| jq '[.[] | select(.role == "admin")]'
curl -s https://api.github.com/repos/owner/repo/issues \
| jq '[.[] | select(.comments > 5)]'
jq '[.[] | select(.email != null)]'
jq '[.[] | select(.active == true and .score >= 80)]'
Mapping and Transformation
echo '[{"name":"alice","age":30},{"name":"bob","age":25}]' \
| jq '[.[] | .name]'
jq 'map(.name)'
jq '[.[] | {user: .name, years: .age}]'
jq '[.[] | . + {senior: (.age > 28)}]'
jq '[.[] | {username: .name, email_address: .email}]'
Aggregation and Reduce
echo '[1, 2, 3, 4, 5]' | jq 'add'
jq '[.[].price] | add'
jq 'length'
jq 'max_by(.score)'
jq 'min_by(.created_at)'
echo '[1,2,3,4,5]' | jq 'reduce .[] as $x (0; . + $x)'
jq 'group_by(.department)'
jq 'group_by(.status) | map({status: .[0].status, count: length})'
String Interpolation and Formatting
jq -r '.[] | "\(.name) is \(.age) years old"'
jq -r '.[] | [.name, .age, .email] | @csv'
jq -r '.[] | [.name, .score] | @tsv'
jq -r '.query | @uri'
jq -r '.data | @base64'
Working with Keys and Paths
jq 'keys'
jq 'has("email")'
jq 'del(.password)'
jq '[.[] | del(.internal_id, .raw_payload)]'
jq '.. | .id? // empty'
jq '[paths(scalars)]'
Conditionals and Error Handling
jq 'if .score >= 90 then "A" elif .score >= 80 then "B" else "C" end'
jq '.nickname // .name'
jq '[.[] | try .nested.value catch null]'
jq '.[] | .optional_field // empty'
Practical Shell Integration
jq '.users' data.json
jq -c '.[]' records.json | while IFS= read -r record; do
echo "Processing: $record"
done
STATUS="active"
jq --arg s "$STATUS" '[.[] | select(.status == $s)]'
jq --argjson threshold 42 '[.[] | select(.value > $threshold)]'
jq -s '.' records.ndjson
jq -s 'add' file1.json file2.json
kubectl get pods -o json | jq '.items[] | {name: .metadata.name, status: .status.phase}'
gh pr list --json number,title | jq -r '.[] | "\(.number)\t\(.title)"'
aws ec2 describe-instances \
| jq -r '.Reservations[].Instances[] | select(.State.Name=="running") | .InstanceId'
docker inspect $(docker ps -q) | jq -r '.[] | "\(.Name)\t\(.Config.Image)"'
Advanced Patterns
jq '[.names, .scores] | transpose | map({name: .[0], score: .[1]})'
jq 'flatten(1)'
jq 'unique_by(.email)'
jq '[.[] | .name] | unique | sort'
jq 'walk(if type == "string" then ascii_downcase else . end)'
export API_KEY=secret
jq -n 'env.API_KEY'
Best Practices
- Always use
-r (raw output) when passing jq results to shell variables or other commands to strip JSON string quotes
- Use
--arg / --argjson to inject shell variables safely — never interpolate shell variables directly into filter strings
- Prefer
map(f) over [.[] | f] for readability
- Use
-c (compact) for newline-delimited JSON pipelines; omit it for human-readable debugging
- Test filters interactively with
jq -n and literal input before embedding in scripts
- Use
empty to drop unwanted elements rather than filtering to null
Security & Safety Notes
jq is read-only by design — it cannot write files or execute commands
- Avoid embedding untrusted JSON field values directly into shell commands; always quote or use
--arg
Common Pitfalls
-
Problem: jq outputs null instead of the expected value
Solution: Check for typos in key names; use keys to inspect actual field names. Remember JSON is case-sensitive.
-
Problem: Numbers are quoted as strings in the output
Solution: Use --argjson instead of --arg when injecting numeric values.
-
Problem: Filter works in the terminal but fails in a script
Solution: Ensure the filter string uses single quotes in the shell to prevent variable expansion. Example: jq '.field' not jq ".field".
-
Problem: add returns null on an empty array
Solution: Use add // 0 or add // "" to provide a fallback default.
-
Problem: Streaming large files is slow
Solution: Use jq --stream or switch to jstream/gron for very large files.
Related Skills
@bash-pro — Wrapping jq calls in robust shell scripts
@bash-linux — General shell pipeline patterns
@github-automation — Using jq with GitHub CLI JSON output
Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.