APIs return JSON. Config files use JSON. Logs are often JSON. The jq tool lets you slice, filter, and transform JSON from the command line—no scripting required.
Basic Syntax#
1
2
3
4
5
| jq 'filter' input.json
# or
cat input.json | jq 'filter'
# or
curl -s api.example.com | jq 'filter'
|
Pretty Print#
1
2
3
4
5
6
7
8
9
10
11
| # Ugly JSON
echo '{"name":"Alice","age":30}' | jq
# Output:
# {
# "name": "Alice",
# "age": 30
# }
# Compact (remove whitespace)
echo '{"name": "Alice"}' | jq -c
# {"name":"Alice"}
|
1
2
3
4
5
6
| echo '{"name":"Alice","age":30}' | jq '.name'
# "Alice"
# Without quotes
echo '{"name":"Alice","age":30}' | jq -r '.name'
# Alice
|
Nested Objects#
1
2
| echo '{"user":{"name":"Alice","email":"alice@example.com"}}' | jq '.user.name'
# "Alice"
|
Arrays#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # Get element by index
echo '[1,2,3,4,5]' | jq '.[0]'
# 1
# Get slice
echo '[1,2,3,4,5]' | jq '.[1:3]'
# [2, 3]
# Get all elements
echo '[{"name":"Alice"},{"name":"Bob"}]' | jq '.[].name'
# "Alice"
# "Bob"
# Wrap in array
echo '[{"name":"Alice"},{"name":"Bob"}]' | jq '[.[].name]'
# ["Alice", "Bob"]
|
Filtering#
1
2
3
4
5
6
| # Select objects matching condition
echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' | jq '.[] | select(.age > 26)'
# {"name": "Alice", "age": 30}
# Multiple conditions
jq '.[] | select(.status == "active" and .role == "admin")'
|
1
2
3
4
5
6
7
8
9
10
11
| # Create new object
echo '{"first":"Alice","last":"Smith"}' | jq '{fullName: "\(.first) \(.last)"}'
# {"fullName": "Alice Smith"}
# Map over array
echo '[1,2,3]' | jq 'map(. * 2)'
# [2, 4, 6]
# Add field
echo '{"name":"Alice"}' | jq '. + {role: "admin"}'
# {"name": "Alice", "role": "admin"}
|
Real-World Examples#
Parse API response:
1
2
3
4
5
6
| curl -s https://api.github.com/users/torvalds | jq '{
name: .name,
company: .company,
repos: .public_repos,
followers: .followers
}'
|
Get all container names:
1
| docker inspect $(docker ps -q) | jq -r '.[].Name'
|
Extract from AWS CLI:
1
2
3
4
| aws ec2 describe-instances | jq -r '
.Reservations[].Instances[] |
"\(.InstanceId)\t\(.State.Name)\t\(.PrivateIpAddress)"
'
|
Filter logs:
1
| cat app.log | jq -c 'select(.level == "error")'
|
Sum values:
1
2
| echo '[{"price":10},{"price":20},{"price":30}]' | jq '[.[].price] | add'
# 60
|
Group by field:
1
2
3
| echo '[{"type":"a","val":1},{"type":"b","val":2},{"type":"a","val":3}]' | \
jq 'group_by(.type) | map({type: .[0].type, total: [.[].val] | add})'
# [{"type":"a","total":4},{"type":"b","total":2}]
|
Conditionals#
1
2
3
4
5
6
7
8
9
10
11
| # If-then-else
echo '{"status":"active"}' | jq 'if .status == "active" then "✓" else "✗" end'
# "✓"
# Default values
echo '{"name":"Alice"}' | jq '.age // 0'
# 0
# Alternative operator
echo 'null' | jq '. // "default"'
# "default"
|
String Operations#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # Split
echo '"a,b,c"' | jq 'split(",")'
# ["a", "b", "c"]
# Join
echo '["a","b","c"]' | jq 'join("-")'
# "a-b-c"
# Length
echo '"hello"' | jq 'length'
# 5
# Contains
echo '"hello world"' | jq 'contains("world")'
# true
# Regex test
echo '"user@example.com"' | jq 'test("@")'
# true
|
Keys and Values#
1
2
3
4
5
6
7
8
| echo '{"a":1,"b":2}' | jq 'keys'
# ["a", "b"]
echo '{"a":1,"b":2}' | jq 'values'
# [1, 2]
echo '{"a":1,"b":2}' | jq 'to_entries'
# [{"key":"a","value":1},{"key":"b","value":2}]
|
Sorting#
1
2
3
4
5
6
7
8
9
10
11
| # Sort array
echo '[3,1,2]' | jq 'sort'
# [1, 2, 3]
# Sort objects by field
echo '[{"name":"Bob"},{"name":"Alice"}]' | jq 'sort_by(.name)'
# [{"name":"Alice"},{"name":"Bob"}]
# Reverse
echo '[1,2,3]' | jq 'reverse'
# [3, 2, 1]
|
Unique#
1
2
3
4
5
| echo '[1,2,2,3,3,3]' | jq 'unique'
# [1, 2, 3]
echo '[{"a":1},{"a":1},{"a":2}]' | jq 'unique_by(.a)'
# [{"a":1},{"a":2}]
|
Multiple Outputs#
1
2
3
4
5
6
7
8
| # Output multiple values
echo '{"a":1,"b":2}' | jq '.a, .b'
# 1
# 2
# Combine into array
echo '{"a":1,"b":2}' | jq '[.a, .b]'
# [1, 2]
|
Reading from Files#
1
2
3
4
5
6
| # Slurp multiple JSON objects into array
jq -s '.' file1.json file2.json
# Process each line as JSON
jq -c '.' <<< '{"a":1}
{"b":2}'
|
1
2
3
4
5
6
7
8
9
10
11
| # Raw string output (no quotes)
echo '{"name":"Alice"}' | jq -r '.name'
# Alice
# Raw input (treat as string, not JSON)
echo 'plain text' | jq -R '.'
# "plain text"
# Null input (for generating JSON)
jq -n '{name: "Alice", age: 30}'
# {"name": "Alice", "age": 30}
|
Combining with Shell#
1
2
3
4
5
6
7
8
9
10
11
12
13
| # Use in shell variable
NAME=$(echo '{"name":"Alice"}' | jq -r '.name')
echo "Hello, $NAME"
# Loop over array
echo '[1,2,3]' | jq -r '.[]' | while read num; do
echo "Number: $num"
done
# Use shell variable in jq
NAME="Alice"
jq -n --arg name "$NAME" '{name: $name}'
# {"name": "Alice"}
|
Error Handling#
1
2
3
4
5
6
7
8
9
10
11
| # Suppress errors
echo 'invalid json' | jq '.' 2>/dev/null
# Check if valid JSON
if echo "$DATA" | jq -e '.' >/dev/null 2>&1; then
echo "Valid JSON"
fi
# Optional object access
echo '{}' | jq '.missing.nested?'
# null (no error)
|
Cheat Sheet#
| Task | Command |
|---|
| Pretty print | jq '.' |
| Get field | jq '.field' |
| Get nested | jq '.a.b.c' |
| Array element | jq '.[0]' |
| All elements | jq '.[]' |
| Filter | jq '.[] | select(.x > 5)' |
| Map | jq 'map(.x * 2)' |
| Keys | jq 'keys' |
| Length | jq 'length' |
| Sort | jq 'sort_by(.field)' |
| Unique | jq 'unique' |
| Raw output | jq -r '.field' |
| Compact | jq -c '.' |
jq turns JSON wrangling from a programming task into a one-liner. Learn the basics and you’ll use it daily.
📬 Get the Newsletter
Weekly insights on DevOps, automation, and CLI mastery. No spam, unsubscribe anytime.