jq — Basics

Key Inspection

List top-level keys of any JSON file
echo '{"name":"eth0","state":"UP","mtu":1500}' | jq 'keys'
# Output:
# [
#   "mtu",
#   "name",
#   "state"
# ]

keys returns a sorted array of key names. Use keys_unsorted to preserve original order.

Count top-level keys
echo '{"name":"eth0","state":"UP","mtu":1500}' | jq 'keys | length'
# Output: 3

Dot Access

Access a single field
echo '{"name":"eth0","state":"UP","mtu":1500}' | jq '.name'
# Output: "eth0"
Access with raw output (-r) — strip quotes for shell use
echo '{"name":"eth0","state":"UP","mtu":1500}' | jq -r '.name'
# Output: eth0

-r (raw output) removes the JSON string quotes. Essential when feeding jq output into shell variables or other commands.

Capture into a shell variable
MTU=$(echo '{"name":"eth0","state":"UP","mtu":1500}' | jq -r '.mtu')
echo "MTU is $MTU"
# Output: MTU is 1500

Nested Paths

Access nested objects with dot chaining
echo '{"iface":{"name":"eth0","addr":{"ipv4":"10.0.0.1","prefix":24}}}' | jq '.iface.addr.ipv4'
# Output: "10.0.0.1"
Access array elements by index
echo '{"ips":["10.0.0.1","10.0.0.2","10.0.0.3"]}' | jq '.ips[0]'
# Output: "10.0.0.1"
Negative index — last element
echo '{"ips":["10.0.0.1","10.0.0.2","10.0.0.3"]}' | jq '.ips[-1]'
# Output: "10.0.0.3"
Array slice — first two elements
echo '{"ips":["10.0.0.1","10.0.0.2","10.0.0.3"]}' | jq '.ips[:2]'
# Output:
# [
#   "10.0.0.1",
#   "10.0.0.2"
# ]

Iterator (.[] )

Iterate over array elements
echo '["eth0","wlan0","lo"]' | jq '.[]'
# Output:
# "eth0"
# "wlan0"
# "lo"

.[] unwraps the array — each element becomes a separate output. This is the foundation of jq pipelines.

Iterate over object values
echo '{"eth0":"UP","wlan0":"DOWN","lo":"UNKNOWN"}' | jq '.[]'
# Output:
# "UP"
# "DOWN"
# "UNKNOWN"
Iterate and extract a field from each object
echo '[{"name":"eth0","state":"UP"},{"name":"wlan0","state":"DOWN"}]' | jq '.[].name'
# Output:
# "eth0"
# "wlan0"

Compact Output (-c)

One JSON object per line — pipeline-friendly
echo '[{"a":1},{"a":2},{"a":3}]' | jq -c '.[]'
# Output:
# {"a":1}
# {"a":2}
# {"a":3}

-c (compact) eliminates whitespace. Use it when piping jq output into while read loops or other line-oriented tools.

Compact for streaming into another jq
echo '[{"name":"eth0","mtu":1500},{"name":"lo","mtu":65536}]' | jq -c '.[]' | jq -r '.name'
# Output:
# eth0
# lo

Identity and Type

Identity filter — pass through unchanged
echo '{"a":1}' | jq '.'
# Output:
# {
#   "a": 1
# }

. is the identity filter. Alone, it pretty-prints. In pipelines, it passes the current value forward.

Check the type of a value
echo '{"name":"eth0","mtu":1500,"active":true,"tags":["prod"],"meta":null}' | \
    jq '[to_entries[] | {key: .key, type: (.value | type)}]'
# Output:
# [
#   {"key": "name", "type": "string"},
#   {"key": "mtu", "type": "number"},
#   {"key": "active", "type": "boolean"},
#   {"key": "tags", "type": "array"},
#   {"key": "meta", "type": "null"}
# ]
Length — works on strings, arrays, and objects
echo '"hello"' | jq 'length'
# Output: 5

echo '[1,2,3]' | jq 'length'
# Output: 3

echo '{"a":1,"b":2}' | jq 'length'
# Output: 2

Pipe Operator

Chain filters with pipe (|)
echo '{"users":[{"name":"evan","role":"admin"},{"name":"test","role":"user"}]}' | \
    jq '.users | .[] | .name'
# Output:
# "evan"
# "test"

| in jq works like shell pipes — the output of the left expression becomes the input of the right. This is how you build multi-step transforms.

Pipe into length for counting
echo '{"users":[{"name":"evan"},{"name":"test"},{"name":"deploy"}]}' | jq '.users | length'
# Output: 3