jq — Construction
Build JSON from Shell Commands
jq -n --arg name "$(hostname)" --arg kernel "$(uname -r)" \
'{"hostname": $name, "kernel": $kernel}'
# Output:
# {
# "hostname": "modestus-razer",
# "kernel": "6.19.10-arch1-1"
# }
--arg name value binds a shell string as a jq variable $name. Always use --arg for strings — it handles quoting and escaping.
jq -n --arg host "$(hostname)" --argjson cpus "$(nproc)" --argjson mem "$(free -m | awk '/Mem:/{print $2}')" \
'{"host": $host, "cpus": $cpus, "memory_mb": $mem}'
# Output:
# {
# "host": "modestus-razer",
# "cpus": 16,
# "memory_mb": 31805
# }
--argjson parses the value as JSON — use it for numbers, booleans, and nested structures. --arg always produces a string.
for iface in $(ls /sys/class/net/); do
jq -n --arg name "$iface" --arg state "$(cat /sys/class/net/$iface/operstate 2>/dev/null || echo unknown)" \
'{"name": $name, "state": $state}'
done | jq -s '.'
# Output: array of interface objects with name and state
The pattern: emit one JSON object per loop iteration, then jq -s '.' slurps them into an array.
Generate JSON with -n
jq -n '{"created": now | strftime("%Y-%m-%dT%H:%M:%SZ"), "version": "1.0"}'
# Output:
# {
# "created": "2026-04-11T...",
# "version": "1.0"
# }
-n (null input) tells jq not to read stdin. Required when generating JSON rather than transforming it.
jq -n '[range(5)]'
# Output: [0, 1, 2, 3, 4]
jq -n '[range(3) | {id: ., label: "item-\(.)"}]'
# Output:
# [
# {"id": 0, "label": "item-0"},
# {"id": 1, "label": "item-1"},
# {"id": 2, "label": "item-2"}
# ]
Object Construction
echo '{"ifname":"eth0","address":"aa:bb:cc:dd:ee:ff","operstate":"UP","mtu":1500,"txqlen":1000}' | \
jq '{name: .ifname, mac: .address, up: (.operstate == "UP")}'
# Output:
# {
# "name": "eth0",
# "mac": "aa:bb:cc:dd:ee:ff",
# "up": true
# }
Object construction {key: expr} creates a new object. The expression after the colon is evaluated in the current context.
echo '{"name":"eth0","state":"UP","mtu":1500,"flags":["BROADCAST","MULTICAST"]}' | \
jq '{name, state}'
# Output:
# {
# "name": "eth0",
# "state": "UP"
# }
When the key name matches the field name, {name} is shorthand for \{name: .name}.
echo '[{"key":"dns","value":"10.0.0.53"},{"key":"ntp","value":"10.0.0.123"}]' | \
jq '[.[] | {(.key): .value}] | add'
# Output:
# {
# "dns": "10.0.0.53",
# "ntp": "10.0.0.123"
# }
Parentheses around the key position evaluate an expression as the key name. add merges the array of single-key objects into one.
to_entries / from_entries
echo '{"dns":"53","http":"80","https":"443"}' | jq 'to_entries'
# Output:
# [
# {"key": "dns", "value": "53"},
# {"key": "http", "value": "80"},
# {"key": "https", "value": "443"}
# ]
to_entries explodes an object into an array of {key, value} pairs. This lets you filter, map, and sort object properties as if they were array elements.
echo '{"dns":"53","http":"80","https":"443","ssh":"22","telnet":"23"}' | \
jq '[to_entries[] | select(.value | tonumber > 50)] | from_entries'
# Output:
# {
# "http": "80",
# "https": "443"
# }
from_entries is the inverse — it takes [{key, value}] and builds an object.
echo '{"ifname":"eth0","operstate":"UP","address":"aa:bb:cc:dd:ee:ff"}' | \
jq 'with_entries(
if .key == "ifname" then .key = "name"
elif .key == "operstate" then .key = "state"
elif .key == "address" then .key = "mac"
else . end
)'
# Output:
# {
# "name": "eth0",
# "state": "UP",
# "mac": "aa:bb:cc:dd:ee:ff"
# }
with_entries(f) is shorthand for to_entries | map(f) | from_entries. Use it to transform keys, filter fields, or modify values while preserving object structure.
Add and Merge
echo '{"name":"eth0"}' | jq '. + {"state":"UP","mtu":1500}'
# Output:
# {
# "name": "eth0",
# "state": "UP",
# "mtu": 1500
# }
+ on objects performs a shallow merge. Right side wins on key conflicts.
echo '[{"name":"eth0","config":{"mtu":1500}},{"name":"eth0","config":{"speed":"1G"}}]' | \
jq '.[0] * .[1]'
# Output:
# {
# "name": "eth0",
# "config": {
# "mtu": 1500,
# "speed": "1G"
# }
# }
* performs recursive merge — nested objects are merged rather than replaced.
echo '{"a":[1,2],"b":[3,4]}' | jq '.a + .b'
# Output: [1, 2, 3, 4]
Update Existing Fields
echo '{"name":"eth0","state":"DOWN","mtu":1500}' | jq '.state = "UP"'
# Output:
# {
# "name": "eth0",
# "state": "UP",
# "mtu": 1500
# }
echo '{"name":"eth0","mtu":1500}' | jq '.mtu |= . * 2'
# Output:
# {
# "name": "eth0",
# "mtu": 3000
# }
|= passes the current value of the field through the expression on the right. .mtu |= . * 2 means "take the current mtu, double it, write it back."
echo '[{"name":"eth0"},{"name":"lo"}]' | jq '[.[] | . + {"monitored": true}]'
# Output:
# [
# {"name": "eth0", "monitored": true},
# {"name": "lo", "monitored": true}
# ]
echo '{"name":"eth0","state":"UP","internal_id":"x9f2"}' | jq 'del(.internal_id)'
# Output:
# {
# "name": "eth0",
# "state": "UP"
# }