jq is sed for JSON — a lightweight, flexible command-line processor that turns messy API responses and config files into exactly the data you need. If you've ever squinted at a wall of minified JSON trying to find one nested field, jq is the tool that makes you wonder how you survived without it.
The mental model is simple: jq is a pipeline. Data flows in from the left, gets transformed by filters, and comes out the right side. The . is your starting point — it means "the whole input." From there you drill down with .field, iterate with .[], and chain filters with |. It's functional programming disguised as a command-line tool, and once the pipe-thinking clicks, you'll reach for jq the way you reach for grep.
We've organized this by the kind of problem you're solving rather than by jq's internal taxonomy. Start with Basic Filters to pull values out of JSON, then move to Array and Object Operations when you need to reshape data. The One-Liners section at the end is where the real magic lives — battle-tested patterns you can copy-paste into your terminal right now.
One thing to internalize early: jq expressions are composable. Every filter takes input and produces output, so you can chain them endlessly with |. That select(...) you wrote? Pipe it into map(...). Pipe that into sort_by(...). It's filters all the way down.
Basic Filters
.
Identity — output the entire input unchanged.field
Extract a top-level field by name.field.nested
Drill into nested objects with dot notation.["hyphenated-key"]
Access fields with special characters in the name.[]
Iterate over all elements in an array or values in an object.[0]
Get the first element of an array.[-1]
Get the last element of an array.[2:5]
Slice an array — elements at index 2, 3, and 4.field?
Try to access a field — suppress errors if it does not exist.[] | .name
Iterate an array and extract a field from each element
Types & Values
length
String length, array count, or number of object keyskeys
Get all keys of an object as a sorted arraykeys_unsorted
Get all keys of an object in original ordervalues
Get all values of an object as an arraytype
Return the type as a string: "object", "array", "string", "number", "boolean", or "null"has("field")
Check if an object has a specific key (returns true/false)in({"a":1})
Check if a key exists in a given objectempty
Produce no output — useful for conditional suppressionenv.VAR
Access environment variable VAR
String Operations
split(",")
Split a string by delimiter into an arrayjoin(",")
Join an array of strings with a delimitertest("regex")
Test if a string matches a regex (returns true/false)match("regex")
Return match object with offset, length, and capturescapture("(?<name>\\w+)")
Named capture groups returned as an objectgsub("old"; "new")
Replace all occurrences of a regex patternsub("old"; "new")
Replace first occurrence of a regex patternascii_downcase
Convert string to lowercaseascii_upcase
Convert string to uppercaseltrimstr("prefix")
Remove a prefix from a string if presentrtrimstr("suffix")
Remove a suffix from a string if present@base64
Encode a string as base64@base64d
Decode a base64 string@uri
Percent-encode a string for URLs@html
Escape HTML special characterstostring
Convert any value to its string representationtonumber
Parse a string as a number
Array Operations
map(f)
Apply filter f to every element and collect resultsmap_values(f)
Apply filter f to every value (works on objects too)select(condition)
Keep only elements where condition is truesort
Sort an array of comparable valuessort_by(.field)
Sort array of objects by a specific fieldreverse
Reverse the order of an arraygroup_by(.field)
Group array elements by a field into sub-arraysunique
Remove duplicate values from a sorted arrayunique_by(.field)
Remove duplicates by a specific fieldflatten
Flatten nested arrays into a single levelflatten(1)
Flatten one level of nestingfirst
Get the first output from a generatorlast
Get the last output from a generatorlimit(n; expr)
Take only the first n outputs from an expressionadd
Sum numbers, concatenate strings, or merge arrays/objectsany(condition)
True if any element satisfies the conditionall(condition)
True if all elements satisfy the conditionmin_by(.field)
Get the element with the smallest field valuemax_by(.field)
Get the element with the largest field valueindices(value)
Get all indices where value appears in the arraycontains([values])
Check if array contains all specified valuesinside([values])
Check if input is contained within the given value
Object Operations
to_entries
Convert {"a":1} to [{"key":"a","value":1}]from_entries
Convert [{"key":"a","value":1}] back to {"a":1}with_entries(f)
Shorthand for to_entries | map(f) | from_entries+ (objects)
Merge two objects — right side wins on key conflicts* (objects)
Recursively merge two objects{name, age}
Select specific fields from an object{newname: .oldname}
Rename a field while extractingdel(.field)
Remove a field from an objectpaths
List all paths to leaf values as arraysgetpath(["a","b"])
Get value at a specific path — like .a.bsetpath(["a","b"]; val)
Set value at a specific pathdelpaths([path])
Delete values at specified paths
Conditionals & Comparisons
if . then A else B end
Conditional expression — else clause is optional// (alternative)
Use the right value if left is null or falsea == b
Equality comparison (deep equality for objects/arrays)a != b
Inequality comparisonand, or, not
Boolean operatorstry f
Run filter f, suppress errors silentlytry f catch msg
Run filter f, use msg expression on errorf as $var | expr
Bind a value to a variable for use in the pipelinereduce .[] as $x (init; update)
Fold an array into a single valuelabel $out | foreach ...
Streaming loop with break support
Output Formatting
-r (--raw-output)
Output raw strings without JSON quotes-c (--compact-output)
Minify — print JSON on a single line-S (--sort-keys)
Sort object keys alphabetically in output-e (--exit-status)
Set exit code based on output: 0 for true/non-null, 1 for false/null-n (--null-input)
Do not read input — useful with --argjson or --slurpfile-s (--slurp)
Read all inputs into a single array--arg name val
Pass a string value as a $name variable--argjson name val
Pass a JSON value as a $name variable--slurpfile name file
Load a JSON file as a $name variable (array)@csv
Format an array of arrays as CSV@tsv
Format an array of arrays as TSV@json
Serialize a value as a JSON string (for embedding)@text
Convert to text output
One-Liners
Real-world patterns you will actually use. Copy, paste, adapt.
One-Liners
jq . file.json
Pretty-print a JSON filejq -r ".[] | .name" file.json
Extract a field from every object in an arrayjq "[.[] | select(.active == true)]"
Filter an array to only matching elementsjq -r ".[] | [.name, .email] | @csv"
Convert JSON array to CSVjq -s "." file1.json file2.json
Merge multiple JSON files into one arrayjq -r "keys[]" file.json
List all top-level keysjq ".[] | select(.price > 100) | .name"
Find names where price exceeds a thresholdjq "group_by(.status) | map({(.[0].status): length}) | add"
Count items per groupjq --arg q "$QUERY" '.[] | select(.name | test($q; "i"))'
Case-insensitive search using a shell variablejq -r "to_entries[] | \"\(.key)=\(.value)\""
Convert JSON object to key=value linesjq "[.[] | {id, name}]"
Reshape objects — keep only specific fieldsjq -r ".results | sort_by(.date) | reverse | .[0]"
Get the most recent entry from a sorted arraycurl -s api.example.com | jq .
Pretty-print API response inlinejq -s "map(.items) | flatten | unique_by(.id)"
Merge and deduplicate paginated API responsesjq 'walk(if type == "string" then gsub("\\n"; " ") else . end)'
Recursively clean up newlines in all string values
Use -r (raw output) whenever you pipe jq into another command. Without it, strings come wrapped in quotes, and those quotes will haunt your shell scripts. jq -r '.name' gives you Alice, not "Alice".
The // operator is jq's null coalescing — it returns the right side when the left is null or false. Perfect for defaults: .config.timeout // 30. It's one of those two-character combos that saves you from writing a full if-then-else ninety percent of the time.
When you need to combine data from multiple files or API responses, reach for --slurp (-s). It reads all inputs into a single array, so you can add objects together, flatten arrays, or zip data across files.
Use --arg and --argjson to pass shell variables into jq safely. Never interpolate shell variables directly into jq expressions — it breaks on special characters and opens you up to injection. jq --arg name "$USER" '.[] | select(.name == $name)' is the right way.
The @base64d filter is a lifesaver when working with Kubernetes secrets or JWTs. Pipe a base64-encoded value through jq -r '.data.password | @base64d' and get the plaintext without leaving your pipeline.
Chain to_entries, map, and from_entries (or use the shorthand with_entries) to transform object keys and values in one pass. Want to prefix every key? with_entries(.key = "prefix_" + .key). It's the object equivalent of map for arrays.
If jq's error messages are cryptic, add debug anywhere in your pipeline to see what's flowing through at that point. It prints to stderr without disrupting your output. Think of it as console.log for jq: .[] | debug | select(.active).