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
FormThe 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

FlagBindsValue is
--arg name value$namealways a string
--argjson name json$nameparsed JSON
--slurpfile name file$namean array of every JSON value in the file
--rawfile name file$namethe file as one string, trailing newline kept
--args a b c$ARGS.positionalremaining argv as strings
--jsonargs 1 {}$ARGS.positionalremaining 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
ToolUse
debugprint ["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_numberthe input line the current value ended on
paths(scalars)enumerate every leaf path when you do not know the shape
type / keys_unsortedcheck an assumption before it becomes an error