Every entry below is a jq filter, what it returns, and a minimal example. Output shown came from jq 1.8.2.

For the guided version with explanations and larger worked examples, read the jq Cookbook.

Run Any Entry Here

The playground runs real jq 1.8.2 on the same employees.json used throughout this reference. Paste any filter or jq command from the entries below into the command line and press Run.

Practice the commands on this page in real jq, right here. Nothing downloads until Start (about 360 KB).

Each exercise gives you a starting command. It runs, but it does not do the task yet: change it until it does.

jq Cookbook: Read Values
  1  Print the company name without quotes
  2  List every employee's email instead of every name
  3  Print only the last employee's name
  4  Show Ada's city, or "unknown" when there is none

Filter JSON With select
  5  Print only the names of people older than 30
  6  Find the Engineering staff who earn more than 120000
  7  Name everyone who has no skills listed
  8  Count the Engineering staff as one number

Reshape JSON Objects
  9  Keep only each employee's name and dept
 10  Show each employee's id as employee_id and name as full_name
 11  Give everyone a 10% raise and show each name with the new salary
 12  Build one object that maps each name to that person's email

Count, Sum, Group, and Sort
 13  Print the average age
 14  Count the employees in each department
 15  List the names from the highest salary to the lowest
 16  List every skill once, sorted

Convert JSON to CSV, TSV, and Text
 17  Print id, name, and salary as CSV rows
 18  Put a header row above the TSV of names and emails
 19  Print a line such as "Ada Lopez earns 145000" for everyone

Use jq in Shell Scripts
 20  Pass Engineering in with --arg and list the people in it
 21  Pass 30 in with --argjson and list the people older than it
 22  Make jq exit with status 1 because nobody works in Legal

Search and Combine JSON
 23  Collect every name in the document, wherever it is
 24  List the paths to every string value
 25  Count the employees in both copies of the file together

Real-World jq Recipes
These files are saved samples: what each network recipe in Learn jq printed, trimmed, with nothing private in them. Run a recipe as Learn jq prints it and press Use to try its jq part here, or name the file yourself, as in jq . pods.json.
  github-repo.json  saved sample of curl --silent https://api.github.com/repos/jqlang/jq
  github-releases.json  saved sample of curl --silent "https://api.github.com/repos/jqlang/jq/releases?per_page=3"
  offices.json  saved sample of curl --silent https://raw.githubusercontent.com/jeffabailey/learn/main/tools/jq/offices.json
  pods.json  saved sample of kubectl get pods -o json
  ec2-instances.json  saved sample of aws ec2 describe-instances

The jq practice needs JavaScript. Every command in the reference also works in jq on your own machine.

Selecting Values

The identity filter and field access. These cover most single-value lookups.

  • . returns the whole input unchanged. Use it to pretty-print.
  • .company returns one field. Returns null when the key is absent.
  • ."odd key" returns a field whose name contains spaces or punctuation.
  • .a.b.c walks nested objects. Any missing level yields null.
  • .a? suppresses the error when the input is not an object.
  • .employees[] emits each array element as a separate result.
  • .employees[0] returns one element by index. Indexes start at 0.
  • .employees[-1] counts from the end.
  • .employees[1:3] slices from index 1 up to but excluding index 3.
  • .. recurses through every value in the document.
jq -r '.employees[0].name' employees.json
Ada Lopez

Filtering With select

select(condition) passes an input through when the condition is true and emits nothing when it is false.

  • select(.age > 30) keeps matching values.
  • select(.dept == "Design") compares for equality.
  • select(.age > 30 and .dept == "Engineering") combines with and, or, and not.
  • select(.name | test("^[ab]"; "i")) matches a regular expression. The second argument holds flags, where i means case-insensitive.
  • select(.skills | index("go")) keeps values whose array contains a member.
  • select(.manager == null) finds explicit nulls.
  • select(has("email")) tests for a key rather than a value.

not is a filter, so it follows a pipe rather than preceding the expression.

jq -r '.employees[] | select(.age > 30) | .name' employees.json
Ada Lopez
Chen Wu

Reshaping Objects

Build a new object, or edit the one you have.

  • {name, dept} keeps only those fields, using the field names as keys.
  • {n: .name, d: .dept} renames fields.
  • .name = "New" sets a value.
  • .age |= . + 1 updates a value with a filter.
  • . + {active: true} merges an object, with the right side winning on conflicts.
  • del(.email) removes a key.
  • with_entries(select(.value != null)) drops null-valued keys.
  • keys returns sorted key names, and keys_unsorted preserves document order.
  • to_entries converts an object to an array of {key, value} pairs, and from_entries reverses it.
jq -c '.employees[0] | {n: .name, d: .dept}' employees.json
{"n":"Ada Lopez","d":"Engineering"}

Arrays and Aggregation

Collect, count, and summarize.

  • [.employees[].name] wraps a stream back into an array.
  • length returns array length, object key count, or string character count.
  • map(.salary) applies a filter to every element.
  • add sums numbers, concatenates strings, or merges objects.
  • min, max, min_by(.age), and max_by(.age) find extremes.
  • sort orders an array, and sort_by(.age) orders by a computed key.
  • reverse flips order. Combine with sort_by for descending order.
  • unique removes duplicates and sorts, and unique_by(.dept) deduplicates on a key.
  • group_by(.dept) returns an array of arrays, grouped by a sorted key.
  • flatten collapses nested arrays, and flatten(1) limits the depth.
  • any and all test a whole array, returning a single boolean.
  • range(0; 5) generates numbers, including the start and excluding the end.

add on an empty array returns null, so guard totals with // 0 when the input may be empty.

jq '[.employees[].salary] | add' employees.json
434000

Strings and Numbers

Convert, format, and interpolate.

  • tostring and tonumber convert between types. tonumber fails on text that is not numeric.
  • ascii_downcase and ascii_upcase change case for ASCII only.
  • ltrimstr("pre") and rtrimstr(".suf") remove a prefix or suffix when present.
  • split(",") divides a string into an array, and join(", ") reverses it.
  • test("regex") returns a boolean, match("regex") returns match details, and capture("(?<name>re)") returns named groups.
  • sub("a"; "b") replaces the first match, and gsub("a"; "b") replaces every match.
  • "Hi \(.name)" interpolates a filter into a string.
  • floor, ceil, round, fabs, and sqrt cover common arithmetic.
  • now returns the current Unix timestamp, and todate formats one as ISO 8601.
jq -r '.employees[] | "\(.name) <\(.email)>"' employees.json | head -2
Ada Lopez <ada@acme.example>
Ben Okafor <ben@acme.example>

Defaults and Error Handling

Keep a filter running when the data is imperfect.

  • .missing // "fallback" substitutes when the left side is null or false.
  • .a? returns nothing instead of erroring when the input type is wrong.
  • try .a catch "bad" catches an error and returns a replacement.
  • error("message") raises an error deliberately.
  • empty produces no output, which removes a value from a stream.
  • halt_error exits with a non-zero status, which is useful in scripts.

// treats false as absent, so it is the wrong operator for a boolean field that may legitimately be false.

jq '.employees[0].address.city // "unknown"' employees.json
"unknown"

Output Formats

Control what reaches stdout.

  • @csv formats an array as one comma-separated row, quoting strings.
  • @tsv formats an array as a tab-separated row.
  • @json serializes a value as a JSON string.
  • @base64 encodes, and @base64d decodes.
  • @uri percent-encodes for use in a URL.
  • @sh quotes a value for safe use in a shell command.

@csv and @tsv require an array of scalars, so build one with [...] first, and pair them with -r to strip the outer quotes.

jq -r '.employees[] | [.name, .dept, .salary] | @csv' employees.json | head -2
"Ada Lopez","Engineering",145000
"Ben Okafor","Engineering",118000

Command-Line Flags

Flags change how jq reads input and writes output.

  • -r prints strings raw, without surrounding quotes.
  • -j prints raw output with no trailing newline.
  • -c prints each result on a single line.
  • -n starts with null input instead of reading stdin, which suits --arg-only invocations.
  • -s reads the entire input stream into one array.
  • -e sets the exit status from the output, returning 1 when the result is null or false.
  • -S sorts object keys in the output.
  • --tab indents with tabs, and --indent N sets a space count from 0 to 7.
  • --arg name value passes a string variable, available as $name.
  • --argjson name value passes parsed JSON rather than a string.
  • --slurpfile name file reads a file into an array variable, and --rawfile reads it as one string.
  • --raw-input treats each input line as a string instead of JSON.
  • -f file.jq reads the filter program from a file.

Always pass shell values through --arg rather than string-concatenating them into the program, which keeps quoting correct.

jq -n --arg dept Design --argjson min 90000 \
  '{dept: $dept, floor: $min}'
{
  "dept": "Design",
  "floor": 90000
}

Error Messages and What They Mean

The text jq prints on stderr, and the usual cause.

  • jq: error: syntax error, unexpected ... means the filter failed to compile. Check for an unclosed bracket or quote.
  • Cannot index array with string ("name") means a .field was applied to an array. Add [] to iterate first.
  • Cannot index string with string ("name") means the value is a string, often because -r output was piped back into jq.
  • Cannot iterate over null means [] was applied to a missing key. Use .a[]? to skip it.
  • null (null) has no keys means keys ran against a missing object.
  • Cannot index number with number (0) means an index was applied to a scalar.
  • parse error: Invalid numeric literal means the input is not valid JSON. Check for a shell prompt or log line mixed into the stream.
  • jq: error (at <stdin>:1) prefixes runtime errors, where the number is the input line.

jq exits 0 on success, 1 when -e is set and the output is null or false, 2 on a usage problem, 3 on a compile error, and 5 after halt_error.

  • The jq Cookbook covers the same filters with explanations and full worked examples.
  • The jq Manual is the authoritative specification for every builtin.
  • jq vs yq covers running these filters on YAML, and where yq needs them translated, such as its missing add.

References

  • jq Manual, the official reference for jq 1.8, covering every builtin, operator, and flag.
  • jq on GitHub, for release notes and the changelog that records behavior changes between versions.