jq Fundamentals

jq processes JSON by passing data through filters. Each filter transforms its input and produces output. Understanding this pipeline model is key to jq mastery.

The Pipeline Model

Input JSON  →  Filter 1  →  Filter 2  →  Filter 3  →  Output

Every jq expression is a filter that: 1. Receives JSON input 2. Transforms it 3. Produces JSON output

Identity Filter .

The simplest filter - outputs input unchanged.

echo '{"name": "server-01"}' | jq '.'

Output:

{
  "name": "server-01"
}

Field Access

Single Field

echo '{"name": "server-01", "ip": "10.0.0.1"}' | jq '.name'

Output: "server-01"

Nested Fields

echo '{"user": {"name": "jdoe", "email": "jdoe@example.com"}}' | jq '.user.name'

Output: "jdoe"

Multiple Levels Deep

echo '{"config": {"network": {"ip": "10.0.0.1"}}}' | jq '.config.network.ip'

Output: "10.0.0.1"

Array Access

By Index (0-based)

echo '["a", "b", "c"]' | jq '.[0]'   # First element
echo '["a", "b", "c"]' | jq '.[1]'   # Second element
echo '["a", "b", "c"]' | jq '.[-1]'  # Last element

Slice

echo '[1,2,3,4,5]' | jq '.[1:3]'   # Elements 1-2 → [2,3]
echo '[1,2,3,4,5]' | jq '.[:3]'   # First 3 → [1,2,3]
echo '[1,2,3,4,5]' | jq '.[-2:]'  # Last 2 → [4,5]

Array Field Access

echo '{"users": ["alice", "bob", "charlie"]}' | jq '.users[0]'

Output: "alice"

Array Iteration []

The [] operator iterates over arrays, producing each element.

echo '["a", "b", "c"]' | jq '.[]'

Output:

"a"
"b"
"c"

With Objects

echo '[{"name": "a"}, {"name": "b"}]' | jq '.[].name'

Output:

"a"
"b"

Pipe Operator |

Chain filters together - output of left becomes input of right.

echo '{"users": [{"name": "alice"}, {"name": "bob"}]}' | jq '.users | .[0] | .name'

Output: "alice"

Equivalent to:

jq '.users[0].name'

Object Construction {}

Build new objects from input data.

echo '{"first": "John", "last": "Doe", "age": 30}' | jq '{name: .first, surname: .last}'

Output:

{
  "name": "John",
  "surname": "Doe"
}

Shorthand Syntax

When key name matches field name:

echo '{"name": "server", "ip": "10.0.0.1", "port": 22}' | jq '{name, ip}'

Output:

{
  "name": "server",
  "ip": "10.0.0.1"
}

Array Construction []

Build new arrays.

echo '{"a": 1, "b": 2, "c": 3}' | jq '[.a, .b, .c]'

Output: [1, 2, 3]

Collect Iterated Values

echo '[{"name": "a"}, {"name": "b"}]' | jq '[.[].name]'

Output: ["a", "b"]

Raw Output -r

Remove quotes from string output.

echo '{"name": "server-01"}' | jq '.name'
# Output: "server-01"

echo '{"name": "server-01"}' | jq -r '.name'
# Output: server-01

Essential for shell scripting:

IP=$(echo '{"ip": "10.0.0.1"}' | jq -r '.ip')
echo $IP  # 10.0.0.1

Null Handling

Missing Fields Return null

echo '{"name": "server"}' | jq '.missing'

Output: null

Alternative Operator //

Provide default for null:

echo '{"name": "server"}' | jq '.port // 22'

Output: 22

Optional Field ?

Suppress errors for missing fields:

echo '{"name": "server"}' | jq '.config?.port?'

Output: null (no error)

Length

# String length
echo '"hello"' | jq 'length'  # 5

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

# Object key count
echo '{"a":1,"b":2}' | jq 'length'  # 2

Keys and Values

echo '{"name": "server", "ip": "10.0.0.1"}' | jq 'keys'
# ["ip", "name"]

echo '{"name": "server", "ip": "10.0.0.1"}' | jq 'values'
# ["server", "10.0.0.1"]

echo '{"name": "server", "ip": "10.0.0.1"}' | jq 'to_entries'
# [{"key":"name","value":"server"},{"key":"ip","value":"10.0.0.1"}]

Has and In

# Check if key exists
echo '{"name": "server"}' | jq 'has("name")'  # true
echo '{"name": "server"}' | jq 'has("port")'  # false

# Check if value in array
echo '["a", "b", "c"]' | jq '"b" | IN(.[])' # requires jq 1.5+
echo '["a", "b", "c"]' | jq 'contains(["b"])'  # true

Practice Exercises

Sample Data

cat << 'EOF' > /tmp/servers.json
{
  "servers": [
    {"name": "web-01", "ip": "10.0.0.1", "port": 80, "status": "active"},
    {"name": "web-02", "ip": "10.0.0.2", "port": 80, "status": "inactive"},
    {"name": "db-01", "ip": "10.0.1.1", "port": 5432, "status": "active"}
  ],
  "metadata": {
    "region": "us-west",
    "environment": "production"
  }
}
EOF

Tasks

  1. Get the region:

    jq '.metadata.region' /tmp/servers.json
  2. Get first server name:

    jq '.servers[0].name' /tmp/servers.json
  3. Get all server IPs:

    jq '.servers[].ip' /tmp/servers.json
  4. Build list of server names:

    jq '[.servers[].name]' /tmp/servers.json
  5. Build simplified server objects:

    jq '.servers[] | {name, ip}' /tmp/servers.json
  6. Get server count:

    jq '.servers | length' /tmp/servers.json

Key Takeaways

  1. . is identity - Pass through unchanged

  2. .field for access - Navigate objects

  3. [] for arrays - Index or iterate

  4. | for chaining - Build pipelines

  5. {} for objects - Construct new shapes

  6. -r for raw strings - Shell scripting

  7. // for defaults - Handle nulls

Next Module

Operators - Comparison, arithmetic, and logical operations.