name: jq
description: jq — the command-line JSON processor and its filter language for slicing, filtering, transforming, and reshaping JSON. Use when extracting fields or nested paths from JSON on the command line, filtering/select-ing arrays of objects, reshaping or building new JSON objects, emitting CSV/TSV with @csv/@tsv, getting raw unquoted strings for shell capture with jq -r, slurping (-s) and aggregating JSON (add, group_by, averages), parsing API responses, NDJSON pipelines (-c), or injecting shell values safely with --arg/--argjson/$ENV. Triggers on mentions of jq, the jq command, the jq filter language, jq -r, .foo/.[] filters, @csv/@tsv, slurp/fromjson, or "filter/transform JSON in the terminal". This is the jq CLI and filter language, NOT a generic JSON tutorial and NOT jq language bindings (jaq, gojq, PyPI jq — defer those to their own docs).
jq - Command-Line JSON Processor
Overview
jq is a small, fast command-line tool that runs a filter program over JSON. You write a
tiny program (the filter), jq reads JSON value(s) from stdin or files, runs the filter once
per input value, and prints each produced value back as JSON. Think of it as sed/awk for
structured JSON instead of lines of text.
Key characteristics:
- A filter = a function from one JSON value to zero, one, or many JSON values
- Composable like a shell pipeline —
|pipes one filter's output into the next - Pure & functional — no mutation; "assignment" returns a modified copy
- Streaming by default — reads whitespace-separated values, runs the filter on each
- Deterministic, no TTY — pure stdin→stdout, ideal for scripts, CI, and agents
Mental model (read this first — it prevents the #1 source of confusion)
A jq program is a filter: an input stream of JSON values → an output stream of JSON values.
- The simplest filter is
.(identity) — it copies input to output, pretty-printed. - A filter can emit 0 values (
empty, a failedselect), 1 value (.foo), or many (.[],range(3)). When a filter emits many values, everything downstream runs once per value (a generator / backtracking model). |pipes, exactly like a Unix shell:f | grunsgon each output off.,forks:f, gemits all off's values, then all ofg's.- jq never mutates the input.
|=and=produce a new value with the change applied.
echo '{"a":1,"b":2}' | jq '.a' # 1 (one value)
echo '[1,2,3]' | jq '.[]' # 1 2 3 (a stream of three)
echo '{"a":1,"b":2}' | jq '.a, .b' # 1 2 (, forks the stream)
echo '[1,2,3]' | jq '[.[] | .+10]' # [11,12,13] (| pipes; [...] re-collects)
Disambiguation: This skill documents the
jqCLI and its filter language — invocation, I/O modes, the filter syntax, and the builtin library. It is not a generic JSON tutorial, and it does not cover jq-compatible reimplementations (jaq,gojq) or library bindings (Pythonjq, etc.) — for those, see their own docs.
Prerequisites
CRITICAL: Before proceeding, verify jq is installed and check the version:
jq --version # prints e.g. "jq-1.7.1"
Version note: This skill is documented against jq 1.7 / 1.7.1 (the modern baseline, the big
release after a 5-year gap). Everything stable at or before jq 1.6 is "bedrock" and shown
unannotated. Features added later are tagged inline as (jq 1.7+) / (jq 1.8+) only where
the version is sourced — see references/version-features.md for
the full feature → minimum-version map. Distros vary widely (many Linux LTS still ship 1.6 or
1.7); always confirm with jq --version. If a function errors with "is not defined", it is
likely newer than your jq.
Install
jq is a single self-contained binary with no runtime dependencies.
# macOS
brew install jq
# Debian/Ubuntu
sudo apt-get install jq
# Fedora/RHEL
sudo dnf install jq
# Arch
sudo pacman -S jq
# Alpine
apk add jq
# Windows
winget install jqlang.jq # or: choco install jq / scoop install jq
# Docker (images moved to ghcr.io in 1.7)
docker run -i ghcr.io/jqlang/jq
# Static binaries / source: https://github.com/jqlang/jq/releases
Homepage https://jqlang.org; online playground https://play.jqlang.org.
Invocation at a Glance
echo '<json>' | jq '<filter>' # most common: pipe JSON in via stdin
jq '<filter>' file.json # read from one or more files
jq -c '<filter>' file.json # compact output (one value per line ⇒ NDJSON)
jq -r '<filter>' # raw output: strings without JSON quotes (shell capture)
jq -n '<filter>' # no input: filter starts from null (generate / use $ARGS,$ENV)
jq -f program.jq file.json # load the filter from a file
jq --arg name "Ada" '<filter>' # inject a shell value as $name (a string)
Usage: jq [options] <filter> [file...]. With no files, jq reads stdin. The filter is a
program in the jq language; the most useful invocation flags:
| Flag | Meaning |
|---|---|
-n / --null-input |
Don't read input; run the filter once with null. For generators, $ARGS, $ENV. |
-r / --raw-output |
Output strings without JSON quotes/escapes (non-strings unchanged). |
-c / --compact-output |
Compact — one value per line ⇒ NDJSON output. |
-s / --slurp |
Read all inputs into a single array; run the filter once on that array. |
-R / --raw-input |
Read each line as a JSON string (not parsed JSON); with -s, the whole input as one string. |
-e / --exit-status |
Set exit code from the last output (see Troubleshooting). |
-j / --join-output |
Implies -r; no newline between outputs. |
-S / --sort-keys |
Sort object keys in output. |
-f file / --from-file |
Load the filter program from a file. |
--arg n v / --argjson n v |
Bind $n to a string / a parsed JSON value. |
Always single-quote the filter in the shell — jq programs use
$,(),[],|,", which the shell would otherwise mangle. Inject data with--arg/--argjson/$ENVrather than string-interpolating shell values into the program (safer; no quoting or injection bugs).
Full flag reference, I/O modes, arg-passing, exit codes, and module path: references/cli.md.
Core Workflows
All examples below are runnable and verified on jq 1.7.1.
1. Pretty-print / validate JSON
jq . reformats (pretty-prints) and exits non-zero on invalid JSON — a cheap validator.
jq . file.json # pretty-print
jq -c . file.json # re-emit compact (one line)
jq -e . file.json >/dev/null # validate: exit ≠ 0 ⇒ invalid JSON
2. Extract a field / nested path (use -r for shell capture)
jq '.user.name' file.json # nested object index → "Ada" (JSON-quoted)
jq '.items[0].id' file.json # array index
jq -r '.user.name' file.json # raw → Ada (no quotes — what you want for the shell)
NAME=$(jq -r '.user.name' file.json) # capture a string into a shell variable
Indexing a missing key yields null (not an error). Use ? to suppress type errors
(.a.b?) and // "default" for a fallback (see Troubleshooting).
3. Filter a list of objects with select / pull a field from each
jq -c '.[] | select(.age > 30)' people.json # one matching object per line
jq -r '.[] | .email' people.json # one email per line
jq 'map(select(.active))' people.json # keep the array shape, filtered
.[] iterates the array into a stream; select(f) keeps a value only when f is truthy
(only false and null are falsy — 0, "", [] are truthy).
4. Reshape / build new objects
jq '[.[] | {id, name: .fullName, active: (.status == "on")}]' people.json
{id} is shorthand for {id: .id}. Object construction inside [ ... ] collects every produced
object back into one array. Computed keys: {(.k): .v}.
5. Emit CSV / TSV (and read line-oriented text)
# JSON array of objects → CSV rows (‑r so quotes/escapes are literal)
jq -r '.[] | [.id, .name, .amount] | @csv' file.json
jq -r '.[] | [.id, .name] | @tsv' file.json # tab-separated
# Lines of text → JSON array (raw input + slurp = the whole stdin as one string)
jq -R -s 'split("\n")' file.txt
@csv/@tsv take an array and produce one delimited row, correctly quoting/escaping.
Format list (no @base32 — it doesn't exist): @text @json @base64 @base64d @uri @csv @tsv @html @sh.
6. Slurp + aggregate (sum, average, group)
printf '{"x":1}\n{"x":2}\n{"x":3}\n' | jq -s 'map(.x) | add' # 6 (slurp NDJSON → array)
jq '[.[].amount] | add' orders.json # sum a field
jq 'map(.score) | add/length' scores.json # average
jq 'group_by(.dept) | map({dept: .[0].dept, n: length})' rows.json # group + count
-s (slurp) reads all input values into one array, then runs the filter once — the standard
way to aggregate across an NDJSON stream or multiple files.
7. Pass shell values safely with --arg, and reuse programs with -f
# Inject a string as $name (no shell-quoting hazards); --argjson for parsed JSON
jq --arg name "Ada Lovelace" '.[] | select(.fullName == $name)' people.json
jq --argjson min 30 '.[] | select(.age >= $min)' people.json
# Read a secret from the environment without shell interpolation
TOKEN=… jq -n '{auth: ("Bearer " + $ENV.TOKEN)}'
# Generate from nothing with -n, then save a reusable program with -f
jq -nc '{ts: now, items: ($ARGS.positional)}' --args a b c
jq -f transform.jq data.json
--arg/--argjson also populate $ARGS.named; --args/--jsonargs fill $ARGS.positional.
$ENV is an object of environment variables — prefer $ENV.TOKEN over a shell "$TOKEN".
Non-Interactive / Agent Usage
jq is excellent for scripts and agents — it never prompts and is pure stdin→stdout.
# Branch on JSON content via exit code (-e sets status from the last output)
if jq -e '.ok' resp.json >/dev/null; then echo healthy; fi
# Test-and-extract with a meaningful exit code; // empty drops nulls
jq -e '.token // empty' resp.json
# NDJSON pipeline: compact out, line tooling friendly
jq -c '.[]' big.json | while read -r line; do …; done
# Pull a second file in without shell interpolation
jq --slurpfile cfg config.json '. + $cfg[0]' data.json
jq --rawfile body message.txt '{subject:"hi", body:$body}'
# Stream a huge document leaf-by-leaf (doesn't fit in memory)
jq -n --stream 'fromstream(1 | truncate_stream(inputs))' huge.json
Agent tips: prefer -r + --arg to feed other tools without quoting bugs; use -c for
NDJSON; use -e to branch on content; use --slurpfile/--rawfile/$ENV to combine inputs and
secrets without interpolation; validate cheaply with jq -e . file >/dev/null. More patterns:
references/cli.md.
Quick Reference
| Item | Purpose |
|---|---|
. |
Identity — copy input to output (pretty-printed) |
.foo · .foo.bar · .["a b"] |
Object index (dot or bracket; bracket for odd keys) |
.[0] · .[-1] · .[2:5] |
Array index (negative from end) / slice (arrays & strings) |
.[] · .[]? |
Iterate array/object values into a stream (? suppresses non-iterable error) |
f | g · f, g |
Pipe g over f's outputs / fork (concatenate streams) |
[ f ] · {a: .x, b: .y} · {id, name} |
Collect into array / build object / shorthand keys |
select(f) · map(f) · map_values(f) |
Keep when truthy / [.[]|f] / per-value update |
add · length · keys · has(k) · type |
Sum/concat · count · sorted keys · key present? · value type |
group_by(f) · sort_by(f) · unique_by(f) · min_by/max_by |
Grouping & ordering by a key |
to_entries · from_entries · with_entries(f) |
Object ↔ [{key,value}] transforms |
// "default" |
Alternative: value of LHS unless null/false, else RHS |
.a |= f · .a = v · .a += 1 · del(.a) |
Update-in-place (copy) / set / arithmetic update / delete |
if C then T elif … else E end |
Conditional (else optional, defaults to ., jq 1.7+) |
try f catch g · f? |
Error handling (f? ≡ try f ⇒ empty on error) |
reduce G as $x (INIT; UPD) · foreach … |
Fold over a stream / fold that emits each step |
@csv @tsv @json @base64 @uri @sh @html |
Format/encode (filter, or string-interp prefix) |
test/match/capture/scan/sub/gsub(re; flags) |
Regex (Oniguruma): flags g i x s m |
now · todate · fromdate · strftime(fmt) |
Time: epoch seconds is the lingua franca |
--arg n v · --argjson n v · $ENV · $ARGS |
Inject shell string / JSON / env / CLI args |
input · inputs |
Pull the next / all remaining input values (with -n) |
Troubleshooting
- Strings print with quotes — that's JSON. Use
-rfor raw output when capturing into a shell variable:NAME=$(jq -r .name f.json). nullvs error — indexing a missing object key yieldsnull(not an error); indexing the wrong type is an error (Cannot index array with "foo"). Suppress with?(.a.b?); supply a fallback with// "default".//is "alternative", not boolean OR —a // byieldsawhenaproduces a value that is not null and not false, elseb(it also swallows errors ina). Great for defaults; boolean OR isor.- Empty output is usually not a crash — a
select/filter matched nothing, you usedempty, or a?swallowed an error. Check the filter, not the install. - Multiple results print one per line — wrap the filter in
[ ... ]to collect them into a single array. - "X is not defined" / "X is not a valid format" — the function/format is newer than your jq
(e.g.
trim/trimstr/tobooleanneed jq 1.8+;abs/pick/debug(msgs)need jq 1.7+). Checkjq --versionand references/version-features.md. (@base32,toarray, anddateadddo not exist in any jq version.) - Big-number math is lossy — since jq 1.7 the literal decimal precision is preserved on
round-trip/compare, but arithmetic still uses IEEE-754 doubles (
1e17 + 10→…020). Don't rely on jq for big-integer math. - Shell quoting — single-quote the program; inject data with
--arg/--argjson, never string concatenation.--argfilewas removed in jq 1.7 — use--slurpfile/--arg. - Date math has no
dateadd— do arithmetic on epoch seconds:now + 86400 | todate. Notegmtime/mktimeuse a broken-down array[year, month(0-11), mday, hour, min, sec, wday, yday](month is 0-indexed).
References
For exhaustive detail, see the bundled reference files:
- references/cli.md — every command-line flag, the I/O / slurp / raw modes,
argument passing & special variables (
$ARGS/$ENV/$__loc__), exit codes, modules & search path, and non-interactive/agent patterns. - references/language.md — the filter language: paths, composition
(
|/,), construction, operators, conditionals & error handling,reduce/foreach, variables & destructuring, functions, assignment/path semantics, string interpolation, and comments. - references/builtins.md — the builtin library grouped by purpose:
types, arrays/objects, strings & regex, the
@fmtformat strings, math, dates/time, streaming, SQL-style (INDEX/IN/JOIN), and debugging/control. - references/version-features.md — feature → minimum jq version map (bedrock ≤1.6 vs. what landed in 1.7 / 1.7.1 / 1.8), removed features, and "what version do I need?" guidance.
Resources
- Help:
jq --help, and the manual at https://jqlang.org/manual/ - Homepage: https://jqlang.org
- Playground: https://play.jqlang.org
- Source / releases: https://github.com/jqlang/jq