Step 10 · Ubuntu · Practice

Doing more with jq

In step 2 you used jq to check whether a file was valid JSON. That is the smallest thing it does. This page is for when you want to pull information out of JSON — reading fields, filtering lists, counting and reshaping.

Nothing to install

jq came with step 2. This page is practice only — there is no script to run. Work through it whenever you are ready.

The dot means “the whole thing”

Every jq command is a small expression describing what you want. The simplest expression is ., which means “everything” — that is why jq . file.json prints the file back unchanged.

Start from the file you made in step 2:

{
  "name": "Lakshmi Prasad",
  "roll_number": "24CS042",
  "image_url": "https://example.com/photos/lakshmi.jpg",
  "sankalpam": "I want to be an expert software builder who solve problems with software solutions plus services"
}

Add a key name after the dot to pull out just that value:

$ jq .name student.json
"Lakshmi Prasad"

$ jq .roll_number student.json
"24CS042"

$ jq .sankalpam student.json
"I want to be an expert software builder who solve problems with software solutions plus services"

Losing the quotation marks

$ jq -r .name student.json
Lakshmi Prasad
What -r does, and why you want it

Without it, jq prints text values with their JSON quotes: "Lakshmi Prasad". -r gives raw output with no quotes, which is what you need when feeding the result into another command.

Several fields at once

# two values, one per line
$ jq -r '.name, .roll_number' student.json

# build a smaller object from the ones you want
$ jq '{name, roll_number}' student.json

# rename a key on the way out
$ jq '{student: .name, roll: .roll_number}' student.json

# list the keys the file contains
$ jq 'keys' student.json

Working with a list

JSON gets more interesting when a file holds many records. Create this one to practise on — it works offline, with no network needed:

$ cat > readings.json <<'JSON'
{
  "site": "north",
  "readings": [
    {"id": 1, "value": 12, "ok": true},
    {"id": 2, "value": 7,  "ok": true},
    {"id": 3, "value": 21, "ok": false},
    {"id": 4, "value": 9,  "ok": true}
  ]
}
JSON

Then work through these in order. Each one builds on the last.

#TaskCommandAnswer
1Print the site name jq -r .site readings.json north
2Every value, one per line jq '.readings[].value' readings.json 12 7 21 9
3Only the readings marked ok jq '.readings[] | select(.ok)' readings.json 3 records
4How many are not ok? jq '[.readings[] | select(.ok == false)] | length' readings.json 1
5Add the values up jq '[.readings[].value] | add' readings.json 49
6The largest value jq '[.readings[].value] | max' readings.json 21
7Reshape into a simpler list jq '[.readings[] | {id, value}]' readings.json [{"id":1,"value":12}, …]

The two pieces of punctuation that do the work

Why tasks 4 to 6 wrap everything in [ ]

.readings[].value produces four separate results, not one list of four. length, add and max need a single list to work on, so the square brackets gather the separate results back into one. Leaving them out is the mistake almost everyone makes first.

Reading a real API

Most web services answer in JSON, so curl and jq work together naturally:

# raw JSON: technically readable, practically not
$ curl -s https://api.github.com/repos/aikaryashala/system_setup

# the same thing, indented and coloured
$ curl -s https://api.github.com/repos/aikaryashala/system_setup | jq

# pull out one field
$ curl -s https://api.github.com/repos/aikaryashala/system_setup | jq -r .full_name
aikaryashala/system_setup

# several fields, as a new object
$ curl -s https://api.github.com/repos/aikaryashala/system_setup \
    | jq '{name: .name, stars: .stargazers_count, language: .language}'

# walk into a list: the name of every public repo
$ curl -s https://api.github.com/users/aikaryashala/repos | jq -r '.[].name'

# filter a list, then count what is left
$ curl -s https://api.github.com/users/aikaryashala/repos \
    | jq '[.[] | select(.fork == false)] | length'

Cheat sheet

ExpressionMeans
.The whole document — use it to check a file is valid
.nameThe value of the name key
.a.bGo one level deeper
.items[]Each item of a list in turn
.items[0]Just the first item
keysThe list of keys
lengthHow many items, or how many characters
addAdd a list of numbers together
min / maxSmallest / largest in a list
select(.ok)Keep only the items where that is true
{a, b}Build a new object from those keys
sort_by(.value)Order a list by one field
-rPrint text without quotation marks
-cPrint each result on one line