About jq
jq is a lightweight and flexible command-line JSON processor. It's like sed, awk, and grep combined, but specifically designed for JSON data. In the modern world of APIs, cloud services, and configuration management, jq is an essential tool for every system administrator and developer.
Why jq is Essential:
- Parse JSON: Extract data from complex JSON structures
- Filter: Select specific fields or array elements
- Transform: Reshape JSON into different structures
- Format: Pretty-print or compact JSON output
- Pipeline: Chain operations for complex queries
- Everywhere: Works with APIs, AWS CLI, kubectl, Docker, config files
Common Use Cases:
- Parsing AWS CLI output (EC2, S3, CloudWatch)
- Working with Kubernetes (kubectl) JSON responses
- Processing API responses from REST services
- Extracting data from Docker inspect output
- Filtering and transforming configuration files
- Converting JSON to CSV or other formats
- Building complex queries in shell scripts
- Debugging API responses and troubleshooting
| Operator | Description |
|---|---|
. |
Identity - outputs input unchanged (pretty-prints) |
.field |
Access object field |
.[0] |
Access array element by index |
.[] |
Iterate over array elements |
.[].field |
Access field in each array element |
| |
Pipe - pass output to next filter |
, |
Output multiple values |
select() |
Filter elements matching condition |
map() |
Apply expression to each element |
keys |
Get object keys or array indices |
length |
Length of array, object, or string |
@csv, @json, @base64 |
Format output in specific format |
| Option | Description |
|---|---|
-r |
Raw output (no quotes on strings) |
-c |
Compact output (no pretty-printing) |
-S |
Sort object keys |
-M |
Monochrome (no colored output) |
-s |
Slurp - read entire input as single array |
-n |
Null input (don't read from stdin) |
-e |
Exit with status based on output |
--arg name value |
Pass variable to jq expression |
-f file |
Read jq program from file |
Example 1Pretty-Print and Basic Access
Format JSON and extract simple fields:
Explanation:
.: Identity filter - pretty-prints JSON.name: Access "name" field-r: Raw output without JSON quotes,operator: Output multiple values- Default output includes quotes for strings
- Raw mode useful for scripting and pipes
jq . as a quick JSON validator and formatter. Pipe any JSON through it to check syntax and make it readable: curl api.example.com/data | jq .
Example 2Working with Arrays
Extract data from JSON arrays:
Explanation:
[0]: Access first array element (zero-indexed)[]: Iterate over all array elements.servers[].hostname: Chain operations- Each array element produces separate output line
- Perfect for feeding to shell loops
- Combine with
-rfor clean output
.[] operator "unwraps" arrays, outputting each element separately. This is crucial for processing collections in shell scripts: for ip in $(jq -r '.servers[].ip' servers.json); do ping -c1 $ip; done
Example 3Filtering with select()
Filter array elements based on conditions:
Explanation:
|: Pipe operator passes data to next filterselect(): Filter elements where condition is true==: Equality comparisonand,or: Logical operatorsstartswith(): String function- Chain multiple filters with pipes
select(.field) (field exists and is truthy), select(.field | not) (field is false/null), select(.num > 10) (numeric comparison). Use contains() for substring matching.
Example 4Building New Objects
Transform JSON into new structures:
Explanation:
{name: .hostname}: Create new object with renamed field{hostname, ip}: Shorthand - keeps field names+: String concatenation[...]: Collect results into array- Computed fields can use expressions
- Perfect for reshaping API responses
Example 5map() and Advanced Transformations
Apply transformations to array elements:
Explanation:
map(expr): Apply expression to each array element- map() with select(): Filter and transform
ascii_upcase: String function (also: ascii_downcase)to_entries: Convert array to key-value pairs- .key gets index, .value gets element
- Powerful for batch transformations
map() is cleaner than [] | when you want to keep results in an array. Compare: .servers | map(.hostname) returns array, .servers[].hostname returns separate values.
Example 6Working with AWS CLI Output
Real-world example: Parse AWS EC2 instance data:
Explanation:
- AWS uses nested array structures
- Chain
[]to navigate nested arrays - Tags are array - use select() to find specific tag
\(): String interpolation in raw output- Complex filters for tag-based selection
- Essential for AWS automation scripts
aws ec2 describe-instances | jq -r '.Reservations[].Instances[] | select(.State.Name=="running") | .PrivateIpAddress'
Example 7Aggregation and Statistics
Calculate statistics from JSON data:
Explanation:
add: Sum all array elementslength: Count elements in arraymax_by(expr): Find element with max valuemin,max: Min/max of arrayadd / length: Calculate average- Combine with select() for conditional counts
group_by(): jq 'group_by(.type) | map({type: .[0].type, count: length, total: map(.value) | add})' - groups by field and aggregates within groups.
Example 8Converting to CSV and Other Formats
Export JSON data to CSV for spreadsheets:
Explanation:
@csv: Format array as CSV@tsv: Format array as tab-separated@base64: Base64 encode string@uri: URL encode string@json: Format as JSON (useful in strings)- Headers added manually with array
kubectl get pods -o json | jq -r '.items[] | [.metadata.name, .status.phase] | @csv' > pods.csv
Example 9Using Variables and Arguments
Pass variables to jq expressions for dynamic queries:
Explanation:
--arg name value: Pass string variable$name: Reference variable in expression--argjson name json: Pass JSON object- Variables make jq expressions reusable
- Essential for shell script integration
- Prevents injection issues vs string concatenation
--arg to pass shell variables to jq. Never concatenate into the jq expression: jq ".servers[] | select(.status == \"$STATUS\")" is dangerous (injection risk). Use: jq --arg s "$STATUS" '.servers[] | select(.status == $s)'
Example 10Complex Real-World Pipeline
Combine jq with other tools in production scripts:
Explanation:
group_by(): Group array elements by fieldjoin(): Join array elements with separator- Multi-line jq expressions with backslash
- Combine jq with while loops for automation
- Generate config files dynamically
- Real-world log analysis and alerting
Output includes quotes, breaking shell variable assignments and loops.
Solution: Use -r (raw output) when feeding jq output to shell: for ip in $(jq -r '.servers[].ip' file.json); do .... Without -r, you get "10.0.1.10" instead of 10.0.1.10.
Using .[] when you want to keep results in array.
Solution: .servers[].name outputs separate values. .servers | map(.name) outputs single array. Use map() when next step needs an array, use [] when processing individually.
Accessing non-existent fields causes errors or unexpected output.
Solution: Use // default for null handling: .field // "N/A". Or select(.field) to skip null entries. Or try-catch: try .field catch "error".
Long jq one-liners become unmaintainable.
Solution: Save complex queries to .jq files with comments: # Extract running instances\n.Reservations[].Instances[] | select(.State.Name == "running"). Run with: jq -f query.jq data.json. Document your expressions!
Processing multi-GB JSON files is slow or runs out of memory.
Solution: Use --stream for streaming parse of large files. Or use jq -c to process line-delimited JSON. For huge datasets, consider specialized tools or databases instead of JSON files.
Build complex queries step by step:
Test each stage before adding next filter. Easier debugging.
Interactive jq playground in browser:
Great for learning, testing expressions, and getting help from community.
jq shines in pipelines:
jq is the glue between JSON-producing tools and shell scripts.
Build library of common jq queries:
Reusable, documented, version-controlled queries.
Check JSON validity to avoid cryptic errors:
jq empty parses JSON but produces no output. Perfect for validation.
When queries fail, examine data structure:
Understanding data structure is key to writing correct queries.
| Task | Command |
|---|---|
| Pretty-print JSON | jq . file.json |
| Access field | jq '.field' file.json |
| Raw output (no quotes) | jq -r '.field' file.json |
| Array element | jq '.[0]' file.json |
| Iterate array | jq '.[]' file.json |
| Filter array | jq '.[] | select(.status == "active")' file.json |
| Map transformation | jq 'map(.field)' file.json |
| Create new object | jq '{name: .field1, value: .field2}' file.json |
| Count elements | jq '. | length' file.json |
| Sum values | jq '[.[].value] | add' file.json |
| Convert to CSV | jq -r '.[] | [.f1, .f2] | @csv' file.json |
| Pass variable | jq --arg var "value" '.[] | select(.f == $var)' file.json |
| Validate JSON | jq empty file.json |
Ready-to-Use jq Patterns
| Scenario | Command Pattern |
|---|---|
| AWS: Running instance IPs | aws ec2 describe-instances | jq -r '.Reservations[].Instances[] | select(.State.Name=="running") | .PrivateIpAddress' |
| Docker: Container IPs | docker inspect $(docker ps -q) | jq -r '.[].NetworkSettings.IPAddress' |
| Kubernetes: Pod names | kubectl get pods -o json | jq -r '.items[].metadata.name' |
| Extract nested field | jq '.data.users[].profile.email' file.json |
| Count by category | jq 'group_by(.category) | map({category: .[0].category, count: length})' |
| Find duplicates | jq 'group_by(.id) | map(select(length > 1))' |
| Merge JSON files | jq -s add file1.json file2.json |
| Deep value search | jq '.. | select(type == "string" and contains("search"))' |