jq — Output Formats
@csv — Comma-Separated Values
echo '[{"name":"eth0","ip":"10.0.0.1","state":"UP"},{"name":"lo","ip":"127.0.0.1","state":"UNKNOWN"}]' | \
jq -r '.[] | [.name, .ip, .state] | @csv'
# Output:
# "eth0","10.0.0.1","UP"
# "lo","127.0.0.1","UNKNOWN"
@csv formats an array as a CSV row with proper quoting. Requires -r to avoid double-quoting the entire line.
echo '[{"name":"eth0","ip":"10.0.0.1"},{"name":"wlan0","ip":"10.0.0.2"}]' | \
jq -r '["NAME","IP"], (.[] | [.name, .ip]) | @csv'
# Output:
# "NAME","IP"
# "eth0","10.0.0.1"
# "wlan0","10.0.0.2"
The comma between two expressions produces both outputs sequentially. The header array comes first, then the data rows.
echo '[{"desc":"has, comma","val":"has \"quotes\""}]' | jq -r '.[] | [.desc, .val] | @csv'
# Output:
# "has, comma","has ""quotes"""
@csv automatically escapes commas inside fields and doubles internal quotes. This is why you use @csv instead of manual string concatenation.
@tsv — Tab-Separated Values
echo '[{"name":"eth0","state":"UP","mtu":1500},{"name":"lo","state":"UNKNOWN","mtu":65536}]' | \
jq -r '["NAME","STATE","MTU"], (.[] | [.name, .state, (.mtu | tostring)]) | @tsv' | column -t
# Output:
# NAME STATE MTU
# eth0 UP 1500
# lo UNKNOWN 65536
@tsv produces tab-delimited output. Pipe to column -t for aligned display. Note: numbers must be converted to strings with tostring before @tsv.
echo '[{"user":"evan","uid":1000},{"user":"deploy","uid":1001}]' | \
jq -r '.[] | [.user, (.uid | tostring)] | @tsv' | awk -F'\t' '{print $1, "has UID", $2}'
# Output:
# evan has UID 1000
# deploy has UID 1001
@html — HTML Entity Escaping
echo '{"title":"5 > 3 && 2 < 4","body":"<script>alert(1)</script>"}' | \
jq -r '.title | @html'
# Output: 5 > 3 && 2 < 4
@html escapes <, >, &, and ' characters. Use when embedding jq output in HTML templates.
@json — Re-encode as JSON String
echo '{"data":[1,2,3]}' | jq '{payload: (.data | @json)}'
# Output:
# {
# "payload": "[1,2,3]"
# }
@json serializes a value into a JSON string. Useful when an API expects a JSON payload encoded as a string field.
@base64 and @base64d
echo '{"secret":"hello-world"}' | jq -r '.secret | @base64'
# Output: aGVsbG8td29ybGQ=
echo '"aGVsbG8td29ybGQ="' | jq -r '@base64d'
# Output: hello-world
echo '{"data":{"username":"ZXZhbg==","password":"c2VjcmV0MTIz"}}' | \
jq -r '.data | to_entries[] | "\(.key): \(.value | @base64d)"'
# Output:
# username: evan
# password: secret123
@uri — URL Encoding
echo '"hello world & goodbye"' | jq -r '@uri'
# Output: hello%20world%20%26%20goodbye
echo '{"q":"jq tutorial","page":"1","lang":"en"}' | \
jq -r 'to_entries | map("\(.key)=\(.value | @uri)") | join("&")'
# Output: q=jq%20tutorial&page=1&lang=en
String Interpolation
echo '{"name":"eth0","state":"UP","mtu":1500}' | \
jq -r '"\(.name) is \(.state) with MTU \(.mtu)"'
# Output: eth0 is UP with MTU 1500
\() inside a jq string evaluates the expression and inserts the result. This is jq’s printf equivalent.
echo '[{"name":"eth0","ip":"10.0.0.1"},{"name":"wlan0","ip":"10.0.0.2"}]' | \
jq -r '.[] | "Interface: \(.name)\n Address: \(.ip)\n"'
# Output:
# Interface: eth0
# Address: 10.0.0.1
#
# Interface: wlan0
# Address: 10.0.0.2
echo '[{"name":"eth0","state":"UP"},{"name":"lo","state":"UNKNOWN"}]' | \
jq -r '.[] | "\(.name): \(if .state == "UP" then "ACTIVE" else "INACTIVE" end)"'
# Output:
# eth0: ACTIVE
# lo: INACTIVE
join — Array to String
echo '["eth0","wlan0","docker0"]' | jq -r 'join(", ")'
# Output: eth0, wlan0, docker0
echo '["10.0.0.1","10.0.0.2","10.0.0.3"]' | jq -r 'join("\n")'
# Output:
# 10.0.0.1
# 10.0.0.2
# 10.0.0.3
split — String to Array
echo '"10.0.0.1/24"' | jq 'split("/") | {ip: .[0], prefix: .[1]}'
# Output:
# {
# "ip": "10.0.0.1",
# "prefix": "24"
# }
echo '"2026-04-11"' | jq -r 'split("-") | join("/")'
# Output: 2026/04/11