JSON is everywhere. APIs return it, configs use it, logs contain it. jq is the Swiss Army knife for processing it all from the command line.
Basic Selection#
1
2
3
4
5
6
7
8
9
10
11
12
13
| # Pretty print
echo '{"name":"alice","age":30}' | jq .
# Extract a field
echo '{"name":"alice","age":30}' | jq '.name'
# Output: "alice"
# Raw output (no quotes)
echo '{"name":"alice","age":30}' | jq -r '.name'
# Output: alice
# Nested fields
echo '{"user":{"name":"alice"}}' | jq '.user.name'
|
Working with Arrays#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # Get all elements
echo '[1,2,3]' | jq '.[]'
# Output:
# 1
# 2
# 3
# Get specific index
echo '["a","b","c"]' | jq '.[1]'
# Output: "b"
# Slice
echo '[1,2,3,4,5]' | jq '.[2:4]'
# Output: [3,4]
# First/last
echo '[1,2,3]' | jq 'first' # 1
echo '[1,2,3]' | jq 'last' # 3
|
Filtering#
1
2
3
4
5
6
7
8
9
10
11
12
13
| # Select objects matching condition
echo '[{"name":"alice","age":30},{"name":"bob","age":25}]' | \
jq '.[] | select(.age > 27)'
# Output: {"name":"alice","age":30}
# Multiple conditions
jq '.[] | select(.status == "active" and .role == "admin")'
# Contains
jq '.[] | select(.tags | contains(["important"]))'
# Regex matching
jq '.[] | select(.email | test("@company\\.com$"))'
|
1
2
3
4
5
6
7
8
9
10
11
12
| # Create new object
echo '{"first":"Alice","last":"Smith"}' | \
jq '{fullName: (.first + " " + .last)}'
# Output: {"fullName":"Alice Smith"}
# Map over array
echo '[1,2,3]' | jq 'map(. * 2)'
# Output: [2,4,6]
# Transform array of objects
echo '[{"name":"alice"},{"name":"bob"}]' | \
jq 'map({user: .name, active: true})'
|
API Response Processing#
1
2
3
4
5
6
7
8
9
10
11
12
| # Extract data from GitHub API
curl -s https://api.github.com/users/torvalds/repos | \
jq '.[] | {name, stars: .stargazers_count, language}' | \
jq -s 'sort_by(.stars) | reverse | .[0:5]'
# Get just names
curl -s https://api.github.com/users/torvalds/repos | \
jq -r '.[].name'
# Count items
curl -s https://api.github.com/users/torvalds/repos | \
jq 'length'
|
Aggregation#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # Sum
echo '[{"value":10},{"value":20},{"value":30}]' | \
jq '[.[].value] | add'
# Output: 60
# Average
jq '[.[].value] | add / length'
# Group by
echo '[{"type":"a","n":1},{"type":"b","n":2},{"type":"a","n":3}]' | \
jq 'group_by(.type) | map({type: .[0].type, total: [.[].n] | add})'
# Count by field
jq 'group_by(.status) | map({status: .[0].status, count: length})'
|
Building Output#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # Concatenate to string
echo '{"host":"db","port":5432}' | \
jq -r '"\(.host):\(.port)"'
# Output: db:5432
# Create CSV
echo '[{"name":"alice","age":30},{"name":"bob","age":25}]' | \
jq -r '.[] | [.name, .age] | @csv'
# Output:
# "alice",30
# "bob",25
# Create TSV
jq -r '.[] | [.name, .age] | @tsv'
# URI encode
jq -r '@uri'
# Base64
jq -r '@base64'
|
Conditional Logic#
1
2
3
4
5
6
7
8
9
10
11
| # If/then/else
echo '{"score":85}' | \
jq 'if .score >= 90 then "A" elif .score >= 80 then "B" else "C" end'
# Alternative operator (default values)
echo '{"name":"alice"}' | jq '.age // 0'
# Output: 0
# Try (suppress errors)
echo '{"a":1}' | jq '.b.c.d // "missing"'
# Output: "missing"
|
Modifying JSON#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # Update field
echo '{"name":"alice","age":30}' | jq '.age = 31'
# Add field
echo '{"name":"alice"}' | jq '. + {active: true}'
# Delete field
echo '{"name":"alice","temp":123}' | jq 'del(.temp)'
# Update nested
echo '{"user":{"name":"alice"}}' | jq '.user.name = "bob"'
# Recursive update
jq '.. | objects | .timestamp |= (. // now)'
|
Multiple Files#
1
2
3
4
5
6
7
8
9
| # Combine objects from files
jq -s '.[0] * .[1]' defaults.json overrides.json
# Process files independently
jq -r '.name' file1.json file2.json
# Slurp into array
jq -s '.' file1.json file2.json
# Output: [{...}, {...}]
|
Stream Processing#
For large files, use streaming:
1
2
3
4
5
| # Process line by line (JSONL)
cat events.jsonl | jq -c 'select(.level == "error")'
# Stream large arrays
jq --stream 'select(.[0][0] == "items")' huge.json
|
Practical Examples#
Parse Docker Output#
1
2
3
4
5
6
7
| # Running containers with ports
docker ps --format '{{json .}}' | \
jq -r 'select(.State == "running") | "\(.Names): \(.Ports)"'
# Container resource usage
docker stats --no-stream --format '{{json .}}' | \
jq -s 'sort_by(.MemPerc | rtrimstr("%") | tonumber) | reverse'
|
Kubernetes Resources#
1
2
3
4
5
6
7
| # Pod status summary
kubectl get pods -o json | \
jq -r '.items[] | "\(.metadata.name): \(.status.phase)"'
# Nodes with capacity
kubectl get nodes -o json | \
jq '.items[] | {name: .metadata.name, cpu: .status.capacity.cpu, memory: .status.capacity.memory}'
|
AWS CLI Output#
1
2
3
4
5
6
7
8
9
10
11
12
| # EC2 instances with tags
aws ec2 describe-instances | \
jq '.Reservations[].Instances[] | {
id: .InstanceId,
type: .InstanceType,
state: .State.Name,
name: (.Tags // [] | map(select(.Key == "Name")) | .[0].Value // "unnamed")
}'
# S3 buckets by size
aws s3api list-buckets | \
jq -r '.Buckets[].Name'
|
Log Analysis#
1
2
3
4
5
6
7
8
9
| # Count errors by type
cat app.jsonl | \
jq -s 'map(select(.level == "error")) | group_by(.error_type) |
map({type: .[0].error_type, count: length}) |
sort_by(.count) | reverse'
# Extract slow requests
cat access.jsonl | \
jq 'select(.response_time_ms > 1000) | {path, response_time_ms}'
|
Useful Options#
1
2
3
4
5
6
7
| jq -r # Raw output (no quotes)
jq -c # Compact output (one line)
jq -s # Slurp (read all into array)
jq -e # Exit non-zero if output is null/false
jq --arg k v # Pass string variable
jq --argjson k v # Pass JSON variable
jq -f file.jq # Read filter from file
|
The jq Cheatsheet#
| Pattern | Purpose |
|---|
.field | Get field |
.[] | Iterate array |
.[0] | First element |
select(cond) | Filter |
map(expr) | Transform each |
{new: .old} | Reshape |
| add | Sum array |
length | Count |
sort_by(.f) | Sort |
group_by(.f) | Group |
@csv | To CSV |
.f // default | With default |
Once jq clicks, you’ll wonder how you ever processed JSON without it.
📬 Get the Newsletter
Weekly insights on DevOps, automation, and CLI mastery. No spam, unsubscribe anytime.