jq Cheatsheet

Strings and Formats

Use this jq reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Splitting and joining

split has two arities and they are different functions. One argument is a literal separator, two arguments is a regex with flags.

jq -cn '"a,b,c" | split(",")'            # ["a","b","c"]
jq -cn '"a,b,c" | split(",") | join("-")'# "a-b-c"
jq -cn '"a1b2c" | split("[0-9]"; null)'  # ["a","b","c"], regex form
jq -cn '"a1b2c" | [splits("[0-9]")]'     # ["a","b","c"], as a stream
jq -cn '"abc" | split("")'               # ["a","b","c"]
jq -cn '["a",1,null] | join("-")'        # "a-1-", numbers stringify, null is ""
FilterSeparatorReturns
split(str)literalan array
split(re; flags)regexan array
splits(re) / splits(re; flags)regexa stream
join(str)—a string, null becomes ""
explode / implode—array of codepoints, and back
trimstr(s)—strips s from both ends, jq 1.7.1+

join errors on an array containing an object or an array. Only strings, numbers, booleans, and null are joinable.

Prefix, suffix, trim, case

jq -cn '"prefix-x" | startswith("pre"), endswith("x")'   # true true
jq -cn '"prefix-x" | ltrimstr("prefix-")'                # "x"
jq -cn '"x.json"   | rtrimstr(".json")'                  # "x"
jq -cn '"abc"      | ltrimstr("zzz")'                    # "abc", no match is a no-op
jq -cn '" x "      | trim, ltrim, rtrim'                 # "x" "x " " x"
jq -cn '"xax"      | trimstr("x")'                       # "a", both ends
jq -cn '"Hello"    | ascii_downcase, ascii_upcase'       # "hello" "HELLO"
jq -cn '"abc"      | test("^a"), (. | contains("bc"))'   # true true
jq -cn '"abcabc"   | index("b"), rindex("b"), indices("b")'  # 1 4 [1,4]

- trim, ltrim, and rtrim were added in jq 1.7.1. On older jq use sub("^\\s+";"") and sub("\\s+$";""). - ltrimstr and rtrimstr strip a literal string, once, not a character set. "aaab" | ltrimstr("a") is "aab". - In jq 1.8 these raise on a non-string input rather than passing it through. 1 | ltrimstr("a") is an error, so guard with if type == "string". - ascii_downcase only touches A-Z. It does not fold É.

Regex: test, match, capture, scan, sub

jq uses Oniguruma, which is close to PCRE. Named groups are (?<name>...).

FilterReturns
test(re) / test(re; flags)a boolean
match(re; flags)an object per match, with offset, length, string, captures
capture(re; flags)an object of the named groups only
scan(re)each match as a string, or an array when the regex has groups
sub(re; str)the first match replaced
gsub(re; str)every match replaced
sub(re; str; flags)same with flags
jq -cn '"foo123" | test("[0-9]+"), test("FOO"; "i")'      # true true
jq -cn '"foo123bar45" | [scan("[0-9]+")]'                 # ["123","45"]
jq -cn '"2024-01-02" | capture("(?<y>[0-9]{4})-(?<m>[0-9]{2})")'
# {"y":"2024","m":"01"}
jq -cn '"foo123" | match("([a-z]+)([0-9]+)")
        | {offset, length, captures: [.captures[].string]}'
# {"offset":0,"length":6,"captures":["foo","123"]}
jq -cn '"a1b2" | [match("[0-9]"; "g").offset]'            # [1,3]
jq -cn '"a-b-c" | sub("-";"+"), gsub("-";"+")'            # "a+b-c" "a+b+c"
jq -cn '"foo123" | sub("(?<n>[0-9]+)"; "[\(.n)]")'        # "foo[123]"

The replacement in sub and gsub is a jq string, so \(...) interpolation works and named captures are in scope as fields of .. That is the only way to reference a group in the replacement, there is no \1.

The flag letters are not PCRE's. This trips up everyone:

FlagMeans in jq
gglobal, all matches
icase insensitive
xextended, whitespace and # comments ignored
nignore empty matches
m. matches newline, PCRE's s
ssingle line, ^→\A and $→\Z
pboth s and m
lprefer the longest match
jq -cn '"a\nb" | test("a.b"), test("a.b"; "m")'   # false then TRUE
jq -cn '"a\nb" | test("^b")'                      # false, ^ is string start
jq -cn '"a b"  | test("a b"; "x")'                # false, x ate the space

Under x, write a literal space as \s or [ ].

Interpolation and @ formats

"\(expr)" embeds any jq expression in a string. An @format before a string literal applies that format to only the interpolated parts, which is how you build safe shell commands and URLs.

jq -rn '{a:1} | "a is \(.a) and \(.a + 1)"'      # a is 1 and 2
jq -rn '@base64 "v=\("hi")"'                      # v=aGk=
jq -r  '.items[] | "\(.id): \(.name) at \(.price)"' data.json
jq -rn '@uri "https://x.dev/s?q=\("a b&c")"'      # ...?q=a%20b%26c
jq -rn '@sh "rm -- \("my file.txt")"'             # rm -- 'my file.txt'
FormatTurns a value into
@texttostring, the default
@jsontojson, a JSON-encoded string
@csvone CSV row from an array, strings quoted, null empty
@tsvone TSV row, tabs and newlines escaped as \t \n
@html<, >, &, ', " escaped
@uripercent-encoded, reserved characters included
@shshell-quoted, an array becomes space-separated words
@base64 / @base64dencode / decode
jq -rn '[1,"a,b",null] | @csv'      # 1,"a,b",
jq -rn '[1,"a\tb",null] | @tsv'     # 1<TAB>a\tb<TAB>
jq -rn '"<a>"  | @html'             # &lt;a&gt;
jq -rn '"it'"'"'s" | @sh'           # 'it'\''s'
jq -rn '"hi" | @base64'             # aGk=
jq -rn '"aGk=" | @base64d'          # hi
jq -rn '{a:1} | @json'              # {"a":1}

@csv and @tsv require an array of scalars and reject nested containers. Pair them with -r, or the row comes out as a quoted JSON string.

Numbers and JSON text as strings

jq -cn '1 | tostring'            # "1"
jq -cn '"2" | tonumber'          # 2
jq -cn '"x" | tonumber'          # error, exit 5
jq -cn '"x" | (tonumber? // null)'   # null, the safe parse
jq -cn '{a:1} | tojson'          # "{\"a\":1}"
jq -cn '"{\"a\":1}" | fromjson'  # {"a":1}
jq -rn '[1,2] | tostring'        # [1,2], same as tojson on a container
jq -cn '"1e3" | tonumber'        # 1E+3, the literal is preserved, not normalized
jq -cn '3.14159 | tostring'      # "3.14159"

- tostring on a container is tojson. On a string it is a no-op, which is why @text never double-quotes. - Parsing embedded JSON is fromjson, and it is the right move for a log field that holds a JSON blob as a string. - Large integers keep their literal text when they pass through untouched, but any arithmetic converts them to IEEE 754 doubles and loses precision past 2⁵³.

echo '{"id":12345678901234567890}' | jq -c '.'        # preserved verbatim
echo '{"id":12345678901234567890}' | jq -c '.id + 0'  # 12345678901234567000