Cheat sheets jq

jq cheat sheet: select, reshape, and format JSON from the shell

jq reads JSON, runs a filter over it, and prints JSON (or plain text, if you ask). This sheet covers the flags you use daily, the filters that pull values out, the ones that reshape data, and the ways to get results back into a shell script. Every flag and builtin below is from the official jq 1.8 manual. We attempt to test on all versions.

what it is and when to use it

Use jq whenever JSON is in a pipe: API responses from curl, output from CLIs with a --json or -o json flag, JSON log lines, and package manifests. A jq program is a filter. It takes an input, produces zero or more outputs, and filters chain with | the way shell commands do.

The examples below use this file as users.json:

users.json
[
  {"name": "ada", "role": "admin", "active": true, "logins": 42},
  {"name": "bob", "role": "dev", "active": false, "logins": 3},
  {"name": "cy", "role": "dev", "active": true, "logins": 17}
]

install and check the version

These are the package commands on the jq download page.

CommandWhat it does
sudo apt-get install jq
Debian or Ubuntu
sudo dnf install jq
Fedora
sudo pacman -S jq
Arch Linux
brew install jq
macOS with Homebrew
jq --version
output
jq-1.8.2
Print the installed version

flags you use daily

FlagWhat it does
-rPrint strings without quotes, for use in shell scripts.
-cOne compact line per output instead of pretty-printing.
-SSort the keys of every object.
-nDon’t read input. The filter starts from null.
-sRead every input into one array (slurp).
-eSet the exit status from the last output (see the scripting section).
--arg name valueMake $name a string inside the filter.
--argjson name textMake $name a JSON value inside the filter.
--tab or --indent nIndent with a tab, or with n spaces (7 at most).
CommandWhat it does
jq . users.json
Pretty-print a file, or anything piped in
jq -S . users.json
Sort keys so two JSON files can be compared with diff

pull values out

CommandWhat it does
jq -r '.[].name' users.json
output
ada
bob
cy
One field from every element
jq '.[0]' users.json
The first element
jq '.[-1]' users.json
The last element
jq '.[0:2]' users.json
A slice: elements 0 and 1
jq -r '.[0].email // "none"' users.json
A default when a value is null or missing

filter

CommandWhat it does
jq -c '.[] | select(.active)' users.json
output
{"name":"ada","role":"admin","active":true,"logins":42}
{"name":"cy","role":"dev","active":true,"logins":17}
Keep only the elements that match
jq -r '.[] | select(.role == "dev" and .logins > 10) | .name' users.json
Combine conditions with and, or, not
jq -r '.[] | select(.name | test("^a")) | .name' users.json
Match a regular expression
jq '[.[] | select(.active)] | length' users.json
Count the matches

reshape

CommandWhat it does
jq -c '.[] | {name, logins}' users.json
Build new objects with only the fields you want
jq -c 'map({user: .name, admin: (.role == "admin")})' users.json
Transform every element of an array
jq -r 'sort_by(.logins) | reverse | .[0].name' users.json
Sort, and take the top entry
jq -c 'group_by(.role) | map({role: .[0].role, count: length})' users.json
output
[{"role":"admin","count":1},{"role":"dev","count":2}]
Group by a field and count each group
jq 'map(.logins) | add' users.json
Add up a field
jq -c 'map(.role) | unique' users.json
Unique values of a field
jq -c 'map(.logins |= . + 1)' users.json
Change one field in place
jq -c 'map(del(.active))' users.json
Remove one field
jq -c '.[0] | keys' users.json
Object keys
jq -c '.[0] | to_entries' users.json
Key/value pairs

output for the shell

CommandWhat it does
jq -r '.[] | [.name, .role, .logins] | @tsv' users.json
output
ada	admin	42
bob	dev	3
cy	dev	17
Tab-separated rows, ready for cut, awk, or column -t
jq -r '.[] | [.name, .role] | @csv' users.json
CSV rows, quoted correctly
jq -r '.[] | "\(.name) has \(.logins) logins"' users.json
String interpolation
jq -r 'map(.name) | join(",")' users.json
Join an array into one string

scripting

CommandWhat it does
role=dev
jq -r --arg role "$role" '.[] | select(.role == $role) | .name' users.json
Pass a shell variable in safely
jq -r --argjson min 10 '.[] | select(.logins >= $min) | .name' users.json
Pass a number or other JSON value in
HOME_DIR=/srv jq -n -r '$ENV.HOME_DIR'
Read an environment variable inside the filter
jq -n -c --arg host example.com --argjson port 8080 '{host: $host, port: $port}'
output
{"host":"example.com","port":8080}
Build JSON from nothing, with values from the shell
jq -e '.[1].active' users.json > /dev/null || echo "bob is inactive"
Exit non-zero if a value is false or null, so the shell can branch on it
jq -s 'length' events.jsonl
Several JSON documents in one file or stream (JSON Lines): slurp them into an array
printf 'a\nb\n' | jq -R .
Plain text lines in, one JSON string per line out

gotchas

  • Without -r, string output keeps its JSON quotes. That is the usual reason a shell variable ends up as "ada" with the quotes.
  • .foo on a missing key returns null, not an error. .foo on something that is not an object, such as an array or a string, is an error. .foo? suppresses that error.
  • // treats false the same as null. .active // true returns true even when .active is false.
  • -e exits with status 1 when the last output is false or null, and 4 when there was no output at all.
  • --arg always makes a string. --arg n 5 then .logins > $n compares a number with the string "5". Use --argjson for numbers.
  • -s reads the whole input into memory before the filter runs.

sources

Checked against the jq 1.8 manual (latest release: jq 1.8.2). On older versions, man jq or the versioned manual for your release lists what you have.