What jq Is
JSON is how programs talk to each other now. APIs return it, config files are written in it, logs are full of it. And in its natural state — the state programs actually send — it looks like this:
$ head -c 200 models-min.json
{"models":[{"capabilities":["completion","tools"],"details":{"context_length":32768,"embedding_length":4096,"families":["ember"],"family":"ember","format":"gguf","parameter_size":"7.2B","parent_model"
That is the first 200 characters of a 2,066-byte
file, and the whole file is one line. Somewhere in
there are five AI models with names, sizes, and
capabilities. You could read it with grep, but
grep doesn't know where one value ends and the next
begins; it matches characters, not structure. What
you want is a tool that understands the structure
and answers questions about it.
jq is that tool. It is a filter in the Unix sense: JSON goes in one end, a filter — a small expression written in jq's own language — says what to do with it, and JSON comes out the other end.
JSON in → filter → JSON out
Everything in this guide is a variation on that shape. The filters get longer, but the shape never changes.
The identity filter: pretty-printer and validator
The simplest filter is a single dot, ., which
means "the input, unchanged." That sounds useless
until you watch what jq does while passing it
through: it parses the JSON and prints it again,
properly indented. Feed jq the one-line file and
ask for just the first model:
$ jq '.models[0]' models-min.json
{
"capabilities": [
"completion",
"tools"
],
"details": {
"context_length": 32768,
"embedding_length": 4096,
"families": [
"ember"
],
"family": "ember",
"format": "gguf",
"parameter_size": "7.2B",
"parent_model": "",
"quantization_level": "Q4_K_M"
},
"digest": "sha256:9f2c41aa07b3d5e89c1042f6a8b7d3e5c0918273645a4b3c2d1e0f9a8b7c6d5e",
"model": "ember-2:7b",
"modified_at": "2026-09-14T09:26:44.221Z",
"name": "ember-2:7b",
"size": 4378221568
}
Without the [0] the same command prints all five
models, each in this shape — 114 lines from a
one-line file. This is most people's first and most
frequent jq command, and it's worth being clear
about why it works: jq cannot print what it hasn't
parsed, so . doubles as a validator. If the
JSON is broken, you get no pretty output — you get
the error, with a position:
$ jq '.' broken.json
jq: parse error: Expected separator between values at line 4, column 37
(broken.json in examples/ is a deliberately
damaged file — a missing comma — used again in the
Errors chapter.) Try that on any file ending in
.json before you trust it; it costs nothing.
Going the other way, -c prints each value
compactly, one per line — the format other programs
usually want:
$ jq -c '.models[0]' models.json
{"capabilities":["completion","tools"],"details":{"context_length":32768,"embedding_length":4096,"families":["ember"],"family":"ember","format":"gguf","parameter_size":"7.2B","parent_model":"","quantization_level":"Q4_K_M"},"digest":"sha256:9f2c41aa07b3d5e89c1042f6a8b7d3e5c0918273645a4b3c2d1e0f9a8b7c6d5e","model":"ember-2:7b","modified_at":"2026-09-14T09:26:44.221Z","name":"ember-2:7b","size":4378221568}
That's exactly how models-min.json was made: it
is jq -c '.' models.json, the same data wearing
the other outfit.
Where jq sits
jq doesn't fetch, store, or edit anything on its own. It lives in the middle of pipelines:
- a file on the left:
jq '…' data.json - a program's output on the left:
some-api-tool | jq '…' - nothing on the left at all, with
-n, when the filter itself builds the value
and its output goes to a terminal, another program, or a file. Chapter 11 is about that life in scripts; the chapters before it are about the filter language itself.
Getting jq
Check first — it's packed with most systems:
$ jq --version
jq-1.7
Every command in this guide was run with jq 1.7
(the version in Debian's repositories) against the
sample files in examples/. If you don't have it:
apt install jq on Debian/Ubuntu, brew install
jq on a Mac, or a single self-contained binary
from the project's releases at jqlang.org. jq 1.6
is still common on older systems; everything here
works there too, except where a chapter says so.
The sample files
The examples/ directory ships the exact files the
chapters use, so every output can be reproduced
byte for byte:
models.json— a model-server API response: five fictional AI models, with a minified twin,models-min.jsoninventory.json— eight fictional machines: names, roles, locations, disks, servicesorders.json— three orders with customers and priced line itemsevents.json— eight log events, one JSON value per linesettings.json— a small service config, used when the guide starts changing JSONbroken.json— deliberately invalid, for the error chapterssummary.jq— a filter in a file, andfilters.txt— the cheat sheet
Run the commands from inside examples/ and your
output should match this guide exactly.
Check: jq '.models | length' models.json prints
5, and jq -e '.' broken.json prints nothing but
the parse error. If both do that, your jq and the
samples agree, and everything from here on will
reproduce.