jq — Process JSON on the Command Line
Practical guide to jq — filter, transform and query JSON on the command line with jq's expressive query language, from pretty-print to select().
jq is a lightweight, flexible JSON processor for the command line – you pipe JSON in from curl, cat or any other command and work with it the same way you already use sed, awk or grep for text. Instead of laboriously dissecting JSON with line-oriented tools, you use jq's own query language: filters you chain together with pipes |, field access via dot notation and select() for targeted filtering. With -r you emit raw strings without JSON quotes – perfect when you want to feed values into shell scripts. This guide walks you through the filters and patterns you actually reach for daily, from pretty-print to aggregating whole datasets.
Basic Usage
jq '.' <file> — Pretty-print a JSON file with syntax highlighting.
jq '.' data.jsoncat <file> | jq '.' — Pretty-print JSON from stdin (pipe from another command).
curl -s https://api.example.com/users | jq '.'jq -c '.' <file> — Compact output — print JSON on a single line.
jq -c '.' data.jsonjq -r '.' <file> — Raw output — print strings without JSON quotes (useful for shell scripts).
jq -r '.name' user.jsonjq -n '<expr>' — Null input mode — evaluate an expression without reading any input.
jq -n '{name: "Alice", age: 30}'jq -s '.' <files> — Slurp mode — read all inputs into an array instead of processing each line separately.
jq -s '.' a.json b.jsonjq -e '<filter>' <file> — Exit with status 1 if the output is false or null. Useful in shell scripts.
jq -e '.active' user.json && echo 'User is active'Field Access & Paths
jq '.<field>' <file> — Access a top-level field by name.
jq '.name' user.jsonjq '.<field>.<nested>' <file> — Access a nested field using dot notation.
jq '.address.city' user.jsonjq '.["<field>"]' <file> — Access a field using bracket notation. Required for fields containing special characters or hyphens.
jq '["content-type"]' headers.jsonjq '.<field>?' <file> — Optional field access — suppress errors if the field does not exist.
jq '.nickname?' user.jsonjq '.a, .b, .c' <file> — Select multiple fields. Outputs each value on a separate line.
jq '.name, .email, .age' user.jsonjq 'path(..<field>)' <file> — Return the path (as an array) to a field anywhere in the structure.
jq 'path(..id?)' data.jsonArray Operations
jq '.[]' <file> — Iterate over all elements of an array or values of an object.
jq '.[]' users.jsonjq '.[<n>]' <file> — Access the element at index n (zero-based). Negative indices count from the end.
jq '.[0]' users.jsonjq '.[-1]' <file> — Access the last element of an array.
jq '.[-1]' items.jsonjq '.[<from>:<to>]' <file> — Slice an array from index 'from' (inclusive) to 'to' (exclusive).
jq '.[2:5]' items.jsonjq 'length' <file> — Return the number of elements in an array, characters in a string, or keys in an object.
jq '.users | length' data.jsonjq 'first' <file> — Return the first element produced by an expression.
jq 'first(.users[])' data.jsonjq 'last' <file> — Return the last element produced by an expression.
jq 'last(.users[])' data.jsonjq 'reverse' <file> — Reverse an array.
jq '.items | reverse' data.jsonjq 'flatten' <file> — Flatten a nested array into a single-level array.
jq '.tags | flatten' data.jsonjq 'flatten(<depth>)' <file> — Flatten a nested array to the specified depth.
jq '.nested | flatten(1)' data.jsonjq 'unique' <file> — Remove duplicate values from an array.
jq '.tags | unique' data.jsonjq 'add' <file> — Sum an array of numbers, concatenate strings, or merge an array of objects.
jq '.prices | add' cart.jsonObject Construction
jq '{<key>: .<field>}' <file> — Construct a new object with a specific key and value.
jq '{id: .id, username: .name}' user.jsonjq '{<field>}' <file> — Shorthand: use field name as both key and value path.
jq '{name, email}' user.jsonjq '{(<expr>): <value>}' <file> — Use a computed expression as an object key.
jq '{(.name): .score}' result.jsonjq '. + {<key>: <value>}' <file> — Add or overwrite a field in an object using the merge operator.
jq '. + {active: true}' user.jsonjq 'del(.<field>)' <file> — Remove a field from an object.
jq 'del(.password)' user.jsonjq 'del(.<field>, .<field2>)' <file> — Remove multiple fields from an object.
jq 'del(.password, .token)' user.jsonFiltering & Select
jq '.[] | select(.<field> == <value>)' <file> — Filter array elements where a field equals a given value.
jq '.[] | select(.active == true)' users.jsonjq '.[] | select(.<field> > <n>)' <file> — Filter elements where a numeric field exceeds a threshold.
jq '.[] | select(.age > 18)' users.jsonjq '.[] | select(.<field> | test("<regex>"))' <file> — Filter elements where a string field matches a regular expression.
jq '.[] | select(.email | test("@gmail\.com$"))' users.jsonjq '.[] | select(.<field> != null)' <file> — Filter elements where a field is not null (field exists and has a value).
jq '.[] | select(.phone != null)' users.jsonjq '.[] | select(has("<field>"))' <file> — Filter elements that contain a specific key.
jq '.[] | select(has("avatar"))' users.jsonjq 'if <cond> then <a> else <b> end' <file> — Conditional expression — return one value or another based on a condition.
jq 'if .active then "enabled" else "disabled" end' user.jsonjq '.<field> // <default>' <file> — Alternative operator — use a default value if the result is null or false.
jq '.nickname // .name' user.jsonTransformation & Map
jq 'map(<expr>)' <file> — Apply an expression to every element of an array. Equivalent to [.[] |
jq '.users | map(.name)' data.jsonjq 'map(select(<cond>))' <file> — Filter an array to elements matching a condition (map + select).
jq '.users | map(select(.active == true))' data.jsonjq 'map_values(<expr>)' <file> — Apply an expression to every value of an object or array.
jq 'map_values(. * 2)' scores.jsonjq '[.[] | <expr>]' <file> — Collect results of an expression into a new array using array construction.
jq '[.items[] | .price]' cart.jsonjq '.[] | {<key>: .<field>}' <file> — Transform each element of an array into a new object shape.
jq '.users[] | {id: .id, label: .name}' data.jsonjq '[.[] | {<key>: .<field>}]' <file> — Transform an array into a new array of reshaped objects.
jq '[.users[] | {id: .id, label: .name}]' data.jsonKeys, Values & Entries
jq 'keys' <file> — Return a sorted array of an object's keys.
jq 'keys' config.jsonjq 'keys_unsorted' <file> — Return an object's keys in their original insertion order.
jq 'keys_unsorted' config.jsonjq 'values' <file> — Return an array of an object's values.
jq '.settings | values' config.jsonjq 'has("<key>")' <file> — Return true if the object contains the given key, or the array has the given index.
jq 'has("email")' user.jsonjq 'in' <file> — Return true if the left-hand value is a key in the right-hand object.
jq '.[] | in({"a":1, "b":2})' keys.jsonjq 'to_entries' <file> — Convert an object to an array of {key, value} pairs.
jq '. | to_entries' config.jsonjq 'from_entries' <file> — Convert an array of {key, value} pairs back into an object.
jq '. | from_entries' pairs.jsonjq 'with_entries(<expr>)' <file> — Apply an expression to each {key, value} pair of an object. Shorthand for to_entries | map(
jq 'with_entries(select(.value != null))' config.jsonSorting, Grouping & Aggregation
jq 'sort' <file> — Sort an array of comparable values.
jq '.scores | sort' data.jsonjq 'sort_by(.<field>)' <file> — Sort an array of objects by a specific field.
jq '.users | sort_by(.name)' data.jsonjq 'sort_by(.<field>) | reverse' <file> — Sort an array by a field in descending order.
jq '.users | sort_by(.age) | reverse' data.jsonjq 'group_by(.<field>)' <file> — Group an array of objects by the value of a specific field.
jq '.orders | group_by(.status)' data.jsonjq 'unique_by(.<field>)' <file> — Remove duplicates from an array based on a specific field.
jq '.users | unique_by(.email)' data.jsonjq 'min_by(.<field>)' <file> — Return the element with the minimum value for a given field.
jq '.products | min_by(.price)' data.jsonjq 'max_by(.<field>)' <file> — Return the element with the maximum value for a given field.
jq '.products | max_by(.price)' data.jsonjq '[.[] | .<field>] | add' <file> — Sum a numeric field across all elements of an array.
jq '[.items[] | .price] | add' cart.jsonjq 'reduce .[] as $x (0; . + $x)' <file> — Reduce an array to a single value using an accumulator. Here: sum all numbers.
jq 'reduce .[] as $x (0; . + $x)' numbers.jsonString Operations
jq 'test("<regex>")' <file> — Return true if a string matches a regular expression.
jq '.email | test("@gmail\.com$")' user.jsonjq 'ascii_downcase' <file> — Convert a string to lowercase.
jq '.name | ascii_downcase' user.jsonjq 'ascii_upcase' <file> — Convert a string to uppercase.
jq '.status | ascii_upcase' item.jsonjq 'ltrimstr("<prefix>")' <file> — Remove a prefix from a string if present.
jq '.url | ltrimstr("https://")' link.jsonjq 'rtrimstr("<suffix>")' <file> — Remove a suffix from a string if present.
jq '.filename | rtrimstr(".json")' file.jsonjq 'startswith("<str>")' <file> — Return true if the string starts with the given prefix.
jq '.url | startswith("https")' link.jsonjq 'endswith("<str>")' <file> — Return true if the string ends with the given suffix.
jq '.filename | endswith(".json")' file.jsonjq 'split("<delim>")' <file> — Split a string into an array on a delimiter.
jq '.tags | split(",")' item.jsonjq 'join("<delim>")' <file> — Join an array of strings into a single string with a delimiter.
jq '.tags | join(", ")' item.jsonjq '"prefix \(.field) suffix"' <file> — String interpolation — embed expressions inside a string using (...).
jq '"Hello, \(.name)! You are \(.age) years old."' user.jsonjq '@base64' <file> — Encode a string as Base64.
jq '.data | @base64' file.jsonjq '@base64d' <file> — Decode a Base64-encoded string.
jq '.encoded | @base64d' file.jsonjq '@uri' <file> — Percent-encode a string for use in URLs.
jq '.query | @uri' search.jsonjq '@csv' <file> — Format an array as a CSV row string.
jq '.rows[] | @csv' data.jsonjq '@tsv' <file> — Format an array as a tab-separated values (TSV) row string.
jq '.rows[] | @tsv' data.jsonjq '@html' <file> — Escape special HTML characters (<, >, &, ', ") in a string.
jq '.body | @html' post.jsonjq '@sh' <file> — Format a string or array as shell-escaped arguments.
jq '@sh "echo \(.name)"' user.jsonTypes & Conversion
jq 'type' <file> — Return the JSON type of a value: null, boolean, number, string, array, or object.
jq '.value | type' data.jsonjq 'tostring' <file> — Convert any value to its JSON string representation.
jq '.count | tostring' data.jsonjq 'tonumber' <file> — Convert a string to a number.
jq '.price | tonumber' item.jsonjq 'not' <file> — Negate a boolean value.
jq '.active | not' user.jsonjq 'infinite' <file> — Produce the IEEE 754 infinity value.
jq -n 'infinite'jq 'nan' <file> — Produce an IEEE 754 NaN value.
jq -n 'nan'jq 'isinfinite' <file> — Return true if a number is infinite.
jq '.value | isinfinite' data.jsonjq 'isnan' <file> — Return true if a value is NaN.
jq '.value | isnan' data.jsonjq 'isnormal' <file> — Return true if a number is a normal finite number.
jq '.value | isnormal' data.jsonRecursive & Path Operations
jq '.. | .<field>?' <file> — Recursively search all descendants for a specific field (recursive descent).
jq '.. | .id?' data.jsonjq '[paths]' <file> — Return all paths in the JSON structure as arrays.
jq '[paths]' config.jsonjq '[leaf_paths]' <file> — Return paths to all leaf (non-container) values.
jq '[leaf_paths]' config.jsonjq 'getpath(["<a>","<b>"])' <file> — Get a value at a specific path given as an array.
jq 'getpath(["address","city"])' user.jsonjq 'setpath(["<a>","<b>"]; <value>)' <file> — Set a value at a specific path given as an array.
jq 'setpath(["address","city"]; "Berlin")' user.jsonjq 'delpaths([["<a>"],["<b>"]])' <file> — Delete values at multiple given paths.
jq 'delpaths([["password"],["token"]])' user.jsonVariables & Definitions
jq '.<field> as $<var> | <expr>' <file> — Assign a value to a named variable for reuse in a later expression.
jq '.total as $t | .items[] | {name, pct: (.price / $t * 100)}' data.jsonjq --arg <var> '<value>' '<expr>' <file> — Pass a shell variable into jq as a string variable.
jq --arg name "Alice" '.[] | select(.name == $name)' users.jsonjq --argjson <var> '<json>' '<expr>' <file> — Pass a shell variable into jq as a parsed JSON value.
jq --argjson minAge 18 '.[] | select(.age >= $minAge)' users.jsonjq 'def <name>: <body>; <expr>' <file> — Define a reusable function inline.
jq 'def double: . * 2; .values[] | double' data.jsonjq -f <file.jq> <file> — Read a jq filter from a file instead of a command-line argument.
jq -f transform.jq data.jsonjq 'env.<VAR>' — Access an environment variable by name.
jq -n 'env.HOME'jq '$ENV.<VAR>' — Access environment variables via the $ENV object.
jq -n '$ENV.PATH'Error Handling
jq 'try <expr>' <file> — Attempt an expression and suppress any errors it produces.
jq '.[] | try .value' mixed.jsonjq 'try <expr> catch <handler>' <file> — Attempt an expression and run a handler if it fails. The error message is available as the input to the handler.
jq 'try tonumber catch "not a number"' values.jsonjq 'error("<message>")' <file> — Raise a custom error and stop processing.
jq 'if .status != "ok" then error("unexpected status") else . end' resp.jsonjq '.[]?' <file> — Iterate with the optional operator — suppress errors if the value is not iterable.
jq '.items[]?' data.jsonPractical Examples
curl -s <api_url> | jq '.' — Pretty-print a JSON API response.
curl -s https://api.github.com/users/octocat | jq '.'jq '.[] | select(.status == "active") | .name' <file> — Extract names of all active users as plain strings (-r for raw output).
jq -r '.[] | select(.status == "active") | .name' users.jsonjq 'del(.[] | .password, .token)' <file> — Remove sensitive fields from every object in an array.
jq 'del(.[] | .password, .token)' users.jsonjq '[.[] | {key: .id, value: .name}] | from_entries' <file> — Convert an array of objects into a lookup map keyed by id.
jq '[.[] | {key: (.id|tostring), value: .name}] | from_entries' users.jsonjq 'group_by(.category) | map({category: .[0].category, count: length})' <file> — Count items per category.
jq 'group_by(.category) | map({category: .[0].category, count: length})' items.jsonjq -r '[.name, .email, .age | tostring] | @csv' <file> — Export selected fields as a CSV row.
jq -r '.[] | [.name, .email, (.age|tostring)] | @csv' users.jsonjq -s '.[0] * .[1]' <file1> <file2> — Deep-merge two JSON objects (the * operator recursively merges objects).
jq -s '.[0] * .[1]' defaults.json overrides.jsonjq 'to_entries | map(select(.value == null)) | map(.key)' <file> — Find all keys with null values in an object.
jq 'to_entries | map(select(.value == null)) | map(.key)' config.jsonjq -r 'path(..)|map(tostring)|join(".")' <file> — Print all key paths in dot notation (useful for exploring deep structures).
jq -r '[path(..)|map(tostring)|join(".")]|unique[]' config.jsonjq -R 'split(" ") | {method: .[0], path: .[1], status: .[2]}' <file> — Parse plain-text lines into JSON objects (-R reads raw strings as input).
jq -R 'split(" ") | {method: .[0], path: .[1]}' access.log Conclusion
jq is read-only and therefore safe by design – it reads JSON but never alters your source file, writing the result to standard output instead. For everyday work a handful of building blocks gets you far: .field for access, | for chaining, select() for filtering and map() for transformation. Take care with raw output via -r: as soon as you feed the values back into the shell, you are responsible for correct quoting yourself, because spaces, newlines or special characters in the data can otherwise break your script. And treat JSON from untrusted sources with the same scepticism as any other foreign input – validate the structure before you rely on specific fields.
Further Reading
- jq manual – the official, complete reference for the jq query language
- jq on Wikipedia – overview of the history and use of jq
- jq play – interactive playground to try out jq filters right in your browser