Advanced

jq --arg, slurp (-s) & recursive descent (..)

3 min · updated June 14, 2026

Three power features that unlock most “advanced” jq.

—arg / —argjson — pass shell values in safely

Never interpolate a shell variable into the filter string. Bind it instead:

name="Ada"
jq --arg name "$name" '.[] | select(.name == $name)' data.json

--arg always makes a string. For numbers, booleans, or JSON, use --argjson:

jq --argjson min 30 '[.[] | select(.age >= $min)]' data.json

slurp (-s) — read all inputs as one array

-s reads the entire input stream (or several files) into a single array:

jq -s '.' a.json b.json          # [ <a>, <b> ]
jq -s 'add' parts/*.json         # merge many arrays/objects into one

Turn newline-delimited JSON (NDJSON) into an array:

jq -s '.' events.ndjson

recursive descent (..) — search at any depth

.. streams every value anywhere in the document. Combine with select to find values wherever they are:

# every "id" value, however deeply nested:
jq '[.. | .id? // empty]' data.json

# every object that has an "error" key, at any depth:
jq '[.. | objects | select(has("error"))]' data.json

paths — where does a value live?

jq 'paths(scalars)' data.json      # every path to a leaf value

Bonus: -n with —arg to build JSON from scratch

jq -n --arg u "$USER" --argjson n 5 '{ user: $u, count: $n }'

--arg is for safety, not just convenience — it avoids quoting/injection bugs from building filter strings in the shell. -s is the answer to “jq only processed the first object” when you have multiple JSON documents. .. plus objects/scalars/select is how you dig through unknown-shaped data.

← All recipes