Why jq Matters
Every API, every log aggregator, every cloud tool outputs JSON. Parsing it with grep and cut is 2008 thinking. jq is sed for JSON—and it’s damn useful.
The catch: jq has a learning curve. But these 5 one-liners handle 80% of real work.
1. Extract a Nested Field
You have a JSON response. You need one field three levels deep. Classic API torture.
curl https://api.example.com/user/123 | jq '.data.user.email'That’s it. .data.user.email is a path. If the user doesn’t exist, you get null. No errors, no fuss.
More complex: get all emails from an array of users:
echo '[{"name":"alice","email":"alice@example.com"},{"name":"bob","email":"bob@example.com"}]' | jq '.[] | .email'.[] iterates. Pipe to .email and you get:
"alice@example.com""bob@example.com"2. Filter by Condition
You have logs. You want only ERROR entries.
cat logs.json | jq '.[] | select(.level == "ERROR")'select() filters. Only entries where .level equals “ERROR” pass through.
Need multiple conditions?
cat logs.json | jq '.[] | select(.level == "ERROR" and .service == "api")'Now you get errors from the api service only.
Want the opposite? Exclude a condition:
cat logs.json | jq '.[] | select(.level != "DEBUG")'3. Restructure (Map)
API returns user data. You only want name and email, and you want to rename fields:
curl https://api.example.com/users | jq '.[] | {username: .name, contact: .email}'Output:
{ "username": "alice", "contact": "alice@example.com"}{ "username": "bob", "contact": "bob@example.com"}{field: .path} syntax creates a new object with whatever fields you want.
4. Combine Multiple Results
You’re calling an API multiple times. Each returns an array. You want them all in one.
(curl api/users; curl api/admins) | jq -s 'add'-s means “slurp”—collect everything into one array. add concatenates arrays.
Output: one big array with all users and admins.
Need a more sophisticated merge?
curl api/user/1 | jq '. + {retrieved_at: now}'. + {key: value} adds a field. now is the current Unix timestamp.
5. Pretty-Print and Compact
Raw JSON is a wall of text. Pretty-print it (default behavior):
curl https://api.example.com/data | jq .Need it compact for storage or piping?
curl https://api.example.com/data | jq -c .-c is compact. One object per line, no whitespace.
Bonus: Conditional Logic
You have a config file. If a field exists, use it; otherwise, use a default:
cat config.json | jq '.timeout // 30'// 30 means: if .timeout is null or missing, use 30. Called the “alternative operator.”
Build a string with conditions:
cat users.json | jq '.[] | "\(.name) <\(.email)>"'Output:
alice <alice@example.com>bob <bob@example.com>String interpolation with \(...) inside double quotes.
The Power Move
Combine everything:
curl https://api.example.com/logs | jq -c '.[] | select(.level == "ERROR") | {time: .timestamp, msg: .message, service: .service} | "\(.time): [\(.service)] \(.msg)"'That pipes stdout to a formatted error report. One line. No Python needed.
6. Flatten Nested Arrays
You’ve got deeply nested JSON and want all values flattened to a single array:
echo '[[1,2],[3,[4,5]]]' | jq 'flatten'# [1, 2, 3, 4, 5]
echo '[[1,2],[3,[4,5]]]' | jq 'flatten(1)'# [1, 2, 3, [4, 5]] -- only one level deepUseful when an API returns paginated results in arrays of arrays.
7. CSV Output
Export JSON as CSV for spreadsheets or awk:
curl https://api.example.com/users | jq -r '.[] | [.name, .email, .role] | @csv'Output:
"alice","alice@example.com","admin""bob","bob@example.com","user"-r removes the outer quotes. @csv handles escaping automatically. Pipe it to a file and open in Excel.
8. Object to Key-Value Pairs
Turn an object into an array of {key, value} pairs — useful for iterating over unknown keys:
echo '{"host":"db.internal","port":5432,"ssl":true}' | jq 'to_entries[]'Output:
{"key": "host", "value": "db.internal"}{"key": "port", "value": 5432}{"key": "ssl", "value": true}Reverse it with from_entries. Handy when you’re building configs dynamically.
Getting Unstuck
jq is expressive but cryptic. When stuck:
- Use
jq '.foo?'instead of.footo avoid errors on missing keys - Use
keysto list object fields:curl api | jq 'keys' - Use
typeto figure out what you’re working with:jq 'type' - Use
|liberally — it’s function composition, same as bash pipes - Add
debuganywhere in a pipeline to dump intermediate values:jq '.foo | debug | .bar'
Ten minutes reading the jq manual answers 90% of use cases. After that, you’re faster with jq than any Python script — and you don’t need to install anything.