jq Cheatsheet
Variables and Functions
Use this jq reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
as $x, binding a value
f as $x | body runs body once per output of f, with $x bound to that output.
The input to body is still whatever was flowing in, not $x.
jq -c '.meta.count as $n | .items | map(.price * $n)' data.json # [30,60,90] jq -cn '1 as $a | 2 as $b | $a + $b' # 3 jq -cn '[(1,2) as $x | $x * 10]' # [10,20] jq -c '.items | length as $n | map(.price / $n)' data.json
- Bindings are immutable. There is no assignment to a variable, only a new binding.
- A generator on the left multiplies the body, which is how as doubles as a
nested loop.
- The point of as is to keep a value while . moves on. Every "I need the parent
while I am inside a child" problem is solved with it.
# refer to the document root from inside an element jq -c '. as $root | .items | map({id, v: $root.version})' data.json # use a container as an argument, which must be bound first jq -c '.meta as $m | "count" | in($m)' data.json # true
Destructuring
A binding pattern can take an array or object apart in place, including nested.
jq -cn '[1,2,3] as [$a,$b] | {$a,$b}' # {"a":1,"b":2} jq -c '.items[0] as {$id, $price} | {$id,$price}' # {"id":1,"price":10} jq -cn '{a:{b:[1,2]}} as {a:{b:[$x,$y]}} | [$x,$y]' # [1,2] jq -c 'reduce .items[] as {$id,$price} ({}; .[$id|tostring] = $price)' data.json jq -cn '{a:1} as {a:$v} | $v' # 1, rename while binding
{$id} is shorthand for {id: $id}, on both the construction and the pattern side.
An element that is not present binds to null instead of failing.
?// tries several patterns in order and uses the first that binds without error.
It is the way to handle a stream of mixed shapes.
jq -cn '[[1,2],{a:3}] | .[] | . as [$x] ?// {a:$x} | $x' # 1 then 3
def, defining a filter
def name: body; defines a zero-argument filter. Arguments are semicolon separated,
and a $-prefixed parameter is bound as a value instead of a filter.
jq -cn 'def double: . * 2; 5 | double' # 10 jq -cn 'def add2($a; $b): $a + $b; add2(1; 2)' # 3 jq -cn 'def twice(f): [f, f]; 3 | twice(. + 1)' # [4,4] jq -c 'def total: reduce .[].price as $p (0; . + $p); .items | total' data.json # 60 jq -cn 'def fact: if . <= 1 then 1 else . * (. - 1 | fact) end; 5 | fact' # 120
| Form | The parameter is |
|---|---|
def f(g): ... | a filter, re-evaluated against . at every use |
def f($v): ... | a value, evaluated once against the caller's input |
def f($v; g): ... | mixed, in any order |
# a filter parameter sees the current input, which is usually what you want jq -cn 'def pluck(f): map(f); [{a:1},{a:2}] | pluck(.a)' # [1,2] # a value parameter is frozen, so it cannot follow the input jq -cn 'def at($k): .[$k]; {a:1} | at("a")' # 1
Definitions are lexically scoped, may be nested inside another def, and may recurse.
Put shared ones in a file and load it with -f, or as a module with -L.
Passing data in from the shell
| Flag | Binds | Value is |
|---|---|---|
--arg name value | $name | always a string |
--argjson name json | $name | parsed JSON |
--slurpfile name file | $name | an array of every JSON value in the file |
--rawfile name file | $name | the file as one string, trailing newline kept |
--args a b c | $ARGS.positional | remaining argv as strings |
--jsonargs 1 {} | $ARGS.positional | remaining argv as JSON |
$ARGS.named | — | every --arg and --argjson as an object |
$ENV / env | — | the environment as an object |
jq -c --arg n api '.name == $n' data.json # true jq -c --argjson lim 15 '[.items[] | select(.price > $lim) | .id]' data.json jq -c --arg lim 15 '[.items[] | select(.price > ($lim|tonumber)) | .id]' data.json jq -cn --args '$ARGS' a b # {"positional":["a","b"],"named":{}} jq -cn --jsonargs '$ARGS.positional' 1 '{"a":2}' # [1,{"a":2}] jq -cn --slurpfile s multi.json '$s' # [{"a":1},{"a":2}] jq -cn --rawfile r raw.txt '$r' # "raw text\n" FOO=bar jq -rn '$ENV.FOO, env.FOO' # bar bar jq -rn '$ENV.NOPE // "unset"' # unset
--arg is always a string. --arg n 5 binds "5", so . > $n compares a
number against a string and jq's total order makes every number less than every
string. Use --argjson for numbers, booleans, null, arrays, and objects.
jq -n --arg n 5 '3 > $n' # false, 3 < "5" in the total order jq -n --argjson n 5 '3 > $n' # false, and this one is real arithmetic jq -n --argjson n 2 '3 > $n' # true
--argfile was removed. Use --slurpfile and index [0], or
--argjson x "$(cat f.json)".
Quoting, and never interpolating the shell
The program is a shell argument, so the shell sees it first. Single-quote the
program and pass every value with --arg.
# right jq --arg name "$USER" '.users[] | select(.name == $name)' data.json # wrong: a value containing a quote or a backslash breaks or injects jq ".users[] | select(.name == \"$USER\")" data.json
- Single quotes protect $, backticks, and \. A program in double quotes has the
shell expand $name before jq ever sees it.
- A program containing a literal single quote is the one awkward case. Use -f
prog.jq, or a quoted heredoc.
jq -f prog.jq data.json jq "$(cat <<'EOF' .items[] | select(.cat == "x") | .id EOF )" data.json
Debugging a program
jq -cn '$__loc__' # {"file":"<top-level>","line":1} jq -c '.items[] | debug | .id' data.json # ["DEBUG:",{...}] to stderr, value passes jq -cn 'def f: debug | . * 2; 3 | f' 2>/dev/null # 6 jq -c '[.items[] | {t: type, k: (keys_unsorted // null)}]' data.json jq --tab '.' data.json | head -5 # read the shape before writing the filter jq -c 'paths(scalars) | join(".")' data.json | head -5
| Tool | Use |
|---|---|
debug | print ["DEBUG:", .] to stderr, pass the value through unchanged |
debug(f) | print f instead of the whole value |
$__loc__ | the file and line, to find which def raised |
input_line_number | the input line the current value ended on |
paths(scalars) | enumerate every leaf path when you do not know the shape |
type / keys_unsorted | check an assumption before it becomes an error |