jq Operators
jq provides a rich set of operators for comparing values, performing arithmetic, combining conditions, and manipulating strings.
Comparison Operators
Equality
# Equal
echo '{"status": "active"}' | jq '.status == "active"' # true
# Not equal
echo '{"status": "active"}' | jq '.status != "inactive"' # true
Numeric Comparison
echo '{"port": 443}' | jq '.port > 1024' # false
echo '{"port": 443}' | jq '.port < 1024' # true
echo '{"port": 443}' | jq '.port >= 443' # true
echo '{"port": 443}' | jq '.port <= 443' # true
Type Comparison
# Check type
echo '"hello"' | jq 'type' # "string"
echo '123' | jq 'type' # "number"
echo 'true' | jq 'type' # "boolean"
echo '[]' | jq 'type' # "array"
echo '{}' | jq 'type' # "object"
echo 'null' | jq 'type' # "null"
# Type testing
echo '"hello"' | jq 'type == "string"' # true
Arithmetic Operators
Basic Math
echo '{"a": 10, "b": 3}' | jq '.a + .b' # 13
echo '{"a": 10, "b": 3}' | jq '.a - .b' # 7
echo '{"a": 10, "b": 3}' | jq '.a * .b' # 30
echo '{"a": 10, "b": 3}' | jq '.a / .b' # 3.333...
echo '{"a": 10, "b": 3}' | jq '.a % .b' # 1 (modulo)
String Concatenation
echo '{"first": "John", "last": "Doe"}' | jq '.first + " " + .last'
# "John Doe"
Array Concatenation
echo '{"a": [1,2], "b": [3,4]}' | jq '.a + .b'
# [1,2,3,4]
Object Merge
echo '{"a": {"x": 1}, "b": {"y": 2}}' | jq '.a + .b'
# {"x": 1, "y": 2}
# Right side wins on conflicts
echo '{}' | jq '{"a": 1} + {"a": 2}'
# {"a": 2}
Increment/Modify
echo '{"count": 5}' | jq '.count += 1' # {"count": 6}
echo '{"count": 5}' | jq '.count -= 1' # {"count": 4}
echo '{"count": 5}' | jq '.count *= 2' # {"count": 10}
Logical Operators
And / Or / Not
# AND
echo '{"a": true, "b": true}' | jq '.a and .b' # true
echo '{"a": true, "b": false}' | jq '.a and .b' # false
# OR
echo '{"a": false, "b": true}' | jq '.a or .b' # true
echo '{"a": false, "b": false}' | jq '.a or .b' # false
# NOT
echo 'true' | jq 'not' # false
echo 'false' | jq 'not' # true
Truthiness
In jq, false and null are falsy. Everything else is truthy.
echo '0' | jq 'if . then "truthy" else "falsy" end' # "truthy"
echo '""' | jq 'if . then "truthy" else "falsy" end' # "truthy"
echo '[]' | jq 'if . then "truthy" else "falsy" end' # "truthy"
echo 'null' | jq 'if . then "truthy" else "falsy" end' # "falsy"
echo 'false' | jq 'if . then "truthy" else "falsy" end' # "falsy"
String Operations
Length
echo '"hello"' | jq 'length' # 5
Split and Join
# Split string into array
echo '"a,b,c"' | jq 'split(",")' # ["a","b","c"]
# Join array into string
echo '["a","b","c"]' | jq 'join(",")' # "a,b,c"
Contains and Inside
# String contains
echo '"hello world"' | jq 'contains("world")' # true
# Array contains
echo '["a","b","c"]' | jq 'contains(["b"])' # true
# Inside (reverse of contains)
echo '"world"' | jq 'inside("hello world")' # true
Starts/Ends With
echo '"server-01"' | jq 'startswith("server")' # true
echo '"server-01"' | jq 'endswith("-01")' # true
Case Conversion
echo '"Hello World"' | jq 'ascii_downcase' # "hello world"
echo '"Hello World"' | jq 'ascii_upcase' # "HELLO WORLD"
Replace (gsub/sub)
# Replace all occurrences
echo '"foo bar foo"' | jq 'gsub("foo"; "baz")' # "baz bar baz"
# Replace first occurrence
echo '"foo bar foo"' | jq 'sub("foo"; "baz")' # "baz bar foo"
# With regex
echo '"server-01"' | jq 'gsub("-[0-9]+"; "")' # "server"
Test (Regex Match)
# Test if matches regex
echo '"192.168.1.1"' | jq 'test("^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")' # true
echo '"not-an-ip"' | jq 'test("^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")' # false
Match (Capture Groups)
# Extract capture groups
echo '"server-web-01"' | jq 'match("server-(.+)-([0-9]+)")'
Output:
{
"offset": 0,
"length": 13,
"string": "server-web-01",
"captures": [
{"offset": 7, "length": 3, "string": "web", "name": null},
{"offset": 11, "length": 2, "string": "01", "name": null}
]
}
Named Captures
echo '"server-web-01"' | jq 'capture("server-(?<role>.+)-(?<num>[0-9]+)")'
Output:
{
"role": "web",
"num": "01"
}
IN and INDEX
IN (Membership Test)
# Check if value is in a set
echo '"critical"' | jq 'IN("critical", "high", "medium")' # true
echo '"low"' | jq 'IN("critical", "high", "medium")' # false
INDEX (Build Lookup Table)
# Create object from array using field as key
echo '[{"id":"a","val":1},{"id":"b","val":2}]' | jq 'INDEX(.id)'
Output:
{
"a": {"id": "a", "val": 1},
"b": {"id": "b", "val": 2}
}
Null Handling
Alternative Operator //
Provides default value when left side is null or false:
echo '{"name": "server"}' | jq '.port // 22'
# 22 (port is null, use default)
echo '{"port": 443}' | jq '.port // 22'
# 443 (port exists, use it)
echo '{"port": 0}' | jq '.port // 22'
# 0 (port is 0, NOT null, use it)
echo '{"enabled": false}' | jq '.enabled // true'
# true (false is falsy, use default)
Error Suppression ?
# Without ?: error on null
echo 'null' | jq '.foo.bar'
# null
# With ?: suppress error
echo 'null' | jq '.foo?.bar?'
# null (no error)
# On arrays (would error without ?)
echo '{"items": null}' | jq '.items[]?'
# (no output, no error)
Math Functions
# Floor, ceil, round
echo '3.7' | jq 'floor' # 3
echo '3.2' | jq 'ceil' # 4
echo '3.5' | jq 'round' # 4
# Absolute value
echo '-5' | jq 'fabs' # 5
# Square root
echo '16' | jq 'sqrt' # 4
# Logarithms
echo '100' | jq 'log10' # 2
echo '2.718' | jq 'log' # ~1
# Power (requires computation)
echo '{"base": 2, "exp": 8}' | jq 'pow(.base; .exp)' # 256
# Min/max in array
echo '[5, 2, 8, 1, 9]' | jq 'min' # 1
echo '[5, 2, 8, 1, 9]' | jq 'max' # 9
Practice Exercises
Sample Data
cat << 'EOF' > /tmp/logs.json
[
{"timestamp": "2026-03-15T10:00:00Z", "severity": "critical", "message": "Auth failed", "count": 5},
{"timestamp": "2026-03-15T10:01:00Z", "severity": "info", "message": "User login", "count": 100},
{"timestamp": "2026-03-15T10:02:00Z", "severity": "high", "message": "Config changed", "count": 2},
{"timestamp": "2026-03-15T10:03:00Z", "severity": "low", "message": "Heartbeat", "count": 1000}
]
EOF
Tasks
-
Find logs where count > 10:
jq '.[] | select(.count > 10)' /tmp/logs.json -
Check if any severity is critical:
jq 'any(.[]; .severity == "critical")' /tmp/logs.json -
Calculate total count:
jq '[.[].count] | add' /tmp/logs.json -
Find logs with "fail" in message (case insensitive):
jq '.[] | select(.message | ascii_downcase | contains("fail"))' /tmp/logs.json -
Extract severity if high or critical:
jq '.[] | select(.severity | IN("high", "critical")) | .message' /tmp/logs.json
Operator Precedence
From highest to lowest:
-
.(field access) -
[](array/object access) -
|(pipe) -
*,/,%(multiplication, division, modulo) -
+,-(addition, subtraction) -
==,!=,<,>,⇐,>=(comparison) -
and -
or -
//(alternative)
Use parentheses to override:
# Without parens
echo '5' | jq '. + 3 * 2' # 11 (3*2 first, then +5)
# With parens
echo '5' | jq '(. + 3) * 2' # 16 ((5+3) first, then *2)
Key Takeaways
-
==for comparison - Not=(that’s assignment/update) -
and/orfor logic - Not&&/|| -
//for defaults - Essential for null handling -
IN()for membership - Cleaner than chainedor -
Regex with
test/match- Full regex support -
String ops are powerful - split, join, gsub
Next Module
Transformations - Object construction and array manipulation.