jq 命令详解与参数实测(Ubuntu 22.04)
jq 命令逐参数实测详解:字段访问与切片、-c/-r/-e/-n/-s/-R/--arg/--argjson/--slurpfile/--args 等选项,select/map/reduce/foreach、路径更新 |=、to_entries、字符串与正则函数、@base64/@csv/@tsv 格式化、exit code 语义与 jq 1.6 已知行为,全部命令与回显在 Ubuntu 22.04(jq 1.6)实测通过。
jq 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 jq 的命令行选项与过滤器语言——字段访问、数组切片、迭代与管道、更新与删除、内置函数、正则与格式化、exit code 语义,全部命令与回显已在 Ubuntu 22.04(jq 1.6)实测通过。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- jq 版本:jq-1.6(
jq --version实测) - 实测工作目录:
/root/jqdemo/,含三个测试数据文件:
users.json(用户数组,后文所有示例的输入):
[
{"id": 1, "name": "alice", "age": 30, "roles": ["admin", "dev"], "active": true, "score": 91.5},
{"id": 2, "name": "bob", "age": 25, "roles": ["dev"], "active": false, "score": 78.0},
{"id": 3, "name": "carol", "age": 35, "roles": ["ops", "dev", "admin"], "active": true, "score": 88.3},
{"id": 4, "name": "dave", "age": 28, "roles": [], "active": true, "score": 95.1},
{"id": 5, "name": "eve", "age": 41, "roles": ["ops"], "active": false, "score": 66.8}
]
config.json(嵌套配置对象):
{
"server": {
"host": "0.0.0.0",
"port": 8080,
"tls": {"enabled": true, "cert": "/etc/ssl/server.pem"}
},
"log": {"level": "info", "file": "/var/log/app.log"},
"features": ["cache", "metrics", "tracing"],
"timeout": 30,
"debug": false,
"comment": null
}
stream.jsonl(JSON Lines 日志流,每行一个独立 JSON 对象):
{"ts": "2026-10-17T09:00:01Z", "level": "INFO", "msg": "request ok", "latency_ms": 12}
{"ts": "2026-10-17T09:00:02Z", "level": "ERROR", "msg": "db timeout", "latency_ms": 3001}
{"ts": "2026-10-17T09:00:03Z", "level": "INFO", "msg": "request ok", "latency_ms": 8}
{"ts": "2026-10-17T09:00:04Z", "level": "WARN", "msg": "slow query", "latency_ms": 850}
{"ts": "2026-10-17T09:00:05Z", "level": "ERROR", "msg": "null pointer", "latency_ms": 5}
二、命令概览
2.1 语法
jq [OPTIONS] <jq filter> [file...]
jq [OPTIONS] --args <jq filter> [strings...]
jq [OPTIONS] --jsonargs <jq filter> [JSON_TEXTS...]
jq 是命令行 JSON 处理器:对每个输入的 JSON 值应用一次过滤器(filter),把结果以 JSON 文本输出。没有指定文件时从标准输入读取。
2.2 命令行选项速查(均已实测)
| 选项 | 作用 | 实测要点 |
|---|---|---|
--version | 打印版本 | 输出 jq-1.6 |
--help | 打印用法 | 输出选项摘要与示例 |
-c / --compact-output | 紧凑输出(单行) | 常用于管道下游 |
-r / --raw-output | 字符串结果去引号原样输出 | 提取纯文本必用 |
-S / --sort-keys | 输出前按键排序 | 对比 JSON 差异方便 |
-a / --ascii-output | 非 ASCII 转义为 \uXXXX | 中文变转义序列 |
-j / --join-output | 同 -r 但不输出换行 | 实测 od -c 确认无 \n |
-n / --null-input | 不读输入,用 null 作唯一输入 | 构造 JSON、配合 --args |
-s / --slurp | 把全部输入读成一个大数组 | JSON Lines 聚合统计 |
-e / --exit-status | 按最后输出值设退出码 | false/null→1,无输出→4 |
-R / --raw-input | 每行文本作为字符串输入 | 处理非 JSON 文本 |
-C / --color-output | 强制彩色输出 | 管道中强制开启实测有效 |
-M / --monochrome-output | 禁止彩色输出 | 默认管道即单色 |
--tab | 用 Tab 缩进 | 默认是 2 空格 |
--indent n | 指定缩进空格数 | 实测 --indent 1 |
--arg name value | 传入字符串变量 $name | filter 中 $name 引用 |
--argjson name json | 传入 JSON 变量 $name | 实测 $n + 1 |
--slurpfile name file | 文件内全部 JSON 值读成数组 $name | 数组套数组注意下标 |
--rawfile name file | 文件全文作为字符串 $name | 行数统计实测 |
--args | 剩余参数变成 $ARGS.positional 字符串数组 | 必须配合 -n,否则 jq 等 stdin |
--jsonargs | 剩余参数按 JSON 解析进 $ARGS.positional | 实测数字与对象混排 |
--stream | 流式解析输出路径-值事件 | 大文件增量处理 |
--unbuffered | 输出不缓冲 | 管道实时消费 |
2.3 过滤器核心语法速查
| 语法 | 含义 |
|---|---|
. | 恒等(原样输出) |
.foo、.foo.bar | 取对象字段,缺失得 null |
.foo? | 容错访问,出错得空而不是报错 |
.[0]、.[-1] | 数组下标(负数从尾数) |
.[1:3]、.[2:] | 数组/字符串切片 |
.[] | 迭代数组所有元素或对象所有值 |
.[]? | 容错迭代 |
, | 逗号连接:对同一输入产生多路输出 |
| | 管道:左侧输出作为右侧输入 |
.. | 递归下降所有子值 |
{name, age} | 对象构造(简写取同名字段) |
[.[] | .x] | 数组构造收集所有输出 |
// | 替代运算符:左侧为 false/null 时取右侧 |
\(...) | 字符串插值 |
if/then/elif/else/end | 条件分支 |
try f catch g、f? | 错误捕获 |
reduce/foreach | 折叠与带状态的迭代 |
|=、=、+= | 路径更新赋值 |
三、参数逐项实测
3.1 --version 与 --help
$ jq --version
jq-1.6
$ jq --help 2>&1 | head -30
jq - commandline JSON processor [version 1.6]
Usage: jq [options] <jq filter> [file...]
jq [options] --args <jq filter> [strings...]
jq [options] --jsonargs <jq filter> [JSON_TEXTS...]
jq is a tool for processing JSON inputs, applying the given filter to
its JSON text inputs and producing the filter's results as JSON on
standard output.
...
3.2 恒等过滤器 . 与字段访问
$ jq '.' config.json | head -8
{
"server": {
"host": "0.0.0.0",
"port": 8080,
"tls": {
"enabled": true,
"cert": "/etc/ssl/server.pem"
}
},
...
$ jq '.server.port' config.json
8080
$ jq '.server.tls.cert' config.json
"/etc/ssl/server.pem"
缺失字段返回 null 而不报错:
$ jq '.server.hostname' config.json
null
? 容错访问:路径中类型不符时返回 null(对象上取值)或空输出,而不是终止报错:
$ jq '.server.port?' config.json
8080
$ jq '.comment.foo?' config.json
null
// 替代运算符:左侧输出为 false 或 null 时取右侧值:
$ jq '.server.hostname // "fallback.local"' config.json
"fallback.local"
SQL 风格的方括号访问与点号等价:
$ jq -r '.["server"]["host"]' config.json
0.0.0.0
链式可选 + 替代组合是读取嵌套配置的安全写法:
$ jq -r '.server?.tls?.cert? // "no-tls"' config.json
/etc/ssl/server.pem
3.3 数组下标、负下标与切片
$ jq '.[2]' users.json
{
"id": 3,
"name": "carol",
"age": 35,
"roles": ["ops", "dev", "admin"],
"active": true,
"score": 88.3
}
$ jq -r '.[-1].name' users.json
eve
$ jq -c '.[1:3] | map(.name)' users.json
["bob","carol"]
$ jq '.[2:] | length' users.json
3
3.4 迭代 .[] 与 .[]?
.[] 对数组逐元素、对对象逐值产生多路输出:
$ jq '.features[]' config.json
"cache"
"metrics"
"tracing"
.[]? 遇到不可迭代值时跳过而不是报错(对混合输入很稳):
$ jq '.[]?.name' users.json
"alice"
"bob"
"carol"
"dave"
"eve"
3.5 -c 紧凑输出
$ jq -c '.[0]' users.json
{"id":1,"name":"alice","age":30,"roles":["admin","dev"],"active":true,"score":91.5}
3.6 -r 原样输出字符串
-r 把字符串结果去引号输出,是 jq 与 shell 其他命令协作的关键开关:
$ jq -r '.server.tls.cert' config.json
/etc/ssl/server.pem
$ jq -r '.[] | .name' users.json
alice
bob
carol
dave
eve
不加 -r 时同样过滤器输出带引号的 JSON 字符串:"alice" 等。
3.7 -S 排序键、--tab、--indent
$ jq -S -c '.log' config.json
{"file":"/var/log/app.log","level":"info"}
$ jq --tab '.log' config.json
{
"level": "info",
"file": "/var/log/app.log"
}
$ jq --indent 1 '.log' config.json
{
"level": "info",
"file": "/var/log/app.log"
}
3.8 -a ASCII 输出
$ echo '{"name":"张三"}' | jq -a '.'
{
"name": "\u5f20\u4e09"
}
3.9 -j 不换行输出
$ printf '%s' '{"a":"x","b":"y"}' | jq -j '.a, .b' | od -c | head -2
0000000 x y
0000002
输出 xy 且末尾无 \n,适合拼接文本。
3.10 -e 按 output 设置退出码
-e 让退出码反映最后一个输出值:false/null 退出码 1,无任何输出(如 empty)退出码 4,其余 0:
$ jq -e '.server.port' config.json >/dev/null; echo "port exists rc=$?"
port exists rc=0
$ jq -e '.server.hostname' config.json >/dev/null; echo "missing rc=$?"
missing rc=1
$ echo 'false' | jq -e '.' >/dev/null; echo "false rc=$?"
false rc=1
$ echo 'null' | jq -e '.' >/dev/null; echo "null rc=$?"
null rc=1
$ echo '"x"' | jq -e '. == "x"' >/dev/null; echo "true rc=$?"
true rc=0
$ jq -e 'empty' config.json >/dev/null 2>&1; echo "empty rc=$?"
empty rc=4
在 shell 脚本里可直接 if jq -e '.enabled' config.json >/dev/null; then ... 做存在性判断。
3.11 -n 空输入模式
-n 不读标准输入,以 null 作为唯一输入,常用于纯计算或构造:
$ jq -n '[1,2,3] | add'
6
$ jq -n '{now: (now | todate)}'
{
"now": "2026-10-17T02:48:15Z"
}
3.12 -s slurp 聚合
-s 把整个输入流(多行 JSON Lines)读成一个数组再求值:
$ jq -s 'length' stream.jsonl
5
$ jq -s 'map(.latency_ms) | add / length' stream.jsonl
775.2
3.13 --arg 与 --argjson 变量传参
$ jq --arg env prod -n '$env'
"prod"
$ jq --argjson n 42 -n '$n + 1'
43
$ jq -r --arg min 90 '.[] | select(.score >= ($min|tonumber)) | .name' users.json
alice
dave
--arg 的值永远是字符串,需要数字时用 tonumber 或改用 --argjson。
3.14 --args 与 --jsonargs 位置参数(实测坑点)
剩余参数进入 $ARGS.positional;$ARGS 完整结构为 {"positional": [...], "named": {...}}:
$ jq -n -r --args '$ARGS.positional' a b c
[
"a",
"b",
"c"
]
$ jq -n -c --args '$ARGS' a b
{"positional":["a","b"],"named":{}}
$ jq -n -c --jsonargs '$ARGS.positional' 1 2
[1,2]
$ jq -n -c --arg x 1 '$ARGS'
{"positional":[],"named":{"x":"1"}}
实测坑点:--args 模式下若忘加 -n,jq 会把剩余参数只当参数、仍尝试从 stdin 读输入;stdin 立即 EOF 时表现为无任何输出且退出码 0(实测复现):
$ jq -r --args '$ARGS.positional' a b c # 没加 -n
(无输出,rc=0)
3.15 --slurpfile 与 --rawfile
--slurpfile 把文件中的全部 JSON 值读成数组——若文件本身是数组,变量就是"数组的数组",两层下标:
$ jq -c --slurpfile u users.json -n '$u[0][0].name'
"alice"
$ jq -c --slurpfile u users.json -n '$u[0] | length'
5
$ jq -r --rawfile s stream.jsonl '($s | split("\n") | length)' -n
6
误用单层下标会报错:jq -n --slurpfile u users.json '$u[0].name' → jq: error (at <unknown>): Cannot index array with string "name"。
3.16 -R 原始文本输入
-R 逐行把文本作为字符串输入,配合 -s 可整文件读成一个字符串:
$ printf 'line1\nline2\n' | jq -R '.'
"line1"
"line2"
$ printf 'line1\nline2\n' | jq -R -s '.'
"line1\nline2\n"
$ printf '1,2,3\n4,5,6\n' | jq -R 'split(",") | map(tonumber)'
[
1,
2,
3
]
[
4,
5,
6
]
3.17 -C / -M 颜色控制
管道中 jq 默认单色;-C 强制彩色(ANSI 转义序列可见),-M 强制单色:
$ jq -C '.' config.json | od -c | head -1
0000000 033 [ 1 ; 3 9 m { \n 033 [ 0 m 033
$ jq -M '.' config.json | head -3
{
"server": {
"host": "0.0.0.0",
3.18 --stream 流式解析
--stream 不构造完整文档,而是输出 [路径, 叶子值] 事件流,适合超大文件按需取值:
$ jq -c --stream 'select(length==2) | .[1] | type' config.json | sort | uniq -c
2 "boolean"
1 "null"
2 "number"
7 "string"
四、过滤器语言实测
4.1 管道与逗号
| 把左侧的每个输出交给右侧;, 对同一输入产生多路输出:
$ jq -c 'map(.name) | length' users.json
5
$ jq '.age | min, max' users.json
25
41
4.2 select / map
$ jq -c 'map(select(.active)) | map(.name)' users.json
["alice","carol","dave"]
$ jq -c '.[] | select(.age > 27 and .age < 40) | .name' users.json
"alice"
"carol"
"dave"
$ jq -c '.[] | select(.roles | index("admin")) | .name' users.json
"alice"
"carol"
select 条件支持 and/or/not,比较运算符与 C 语言一致。
4.3 length / keys / has / type
$ jq 'length' users.json
5
$ jq '.[0].roles | length' users.json
2
$ jq 'keys' config.json
[
"comment",
"debug",
"features",
"log",
"server",
"timeout"
]
$ jq 'keys_unsorted' config.json
[
"server",
"log",
"features",
"timeout",
"debug",
"comment"
]
$ jq '.server | has("port"), has("hostname")' config.json
true
false
$ jq '.comment | type, .timeout | type' config.json
"null"
"number"
keys 排序输出,keys_unsorted 保持原始顺序;type 返回 "null"/"boolean"/"number"/"string"/"array"/"object" 之一。
4.4 paths / leaf_paths / getpath / setpath / delpaths
$ jq -c '[paths] | .[0:4]' config.json
[["server"],["server","host"],["server","port"],["server","tls"]]
$ jq -c '[leaf_paths] | .[0:3]' config.json
[["server","host"],["server","port"],["server","tls","enabled"]]
$ jq -c 'getpath(["server","port"])' config.json
8080
$ jq -c 'setpath(["server","port"]; 9090) | .server.port' config.json
9090
$ jq -c 'delpaths([["comment"],["debug"]]) | has("comment")' config.json
false
4.5 add / min / max / range
$ jq 'map(.age) | add' users.json
159
$ jq 'map(.score) | min, max' users.json
66.8
95.1
$ jq -c '[range(3)]' <<< 'null'
[0,1,2]
4.6 sort_by / group_by / unique / reverse / flatten
$ jq -c 'sort_by(.age) | map(.name)' users.json
["bob","dave","alice","carol","eve"]
$ jq -c 'map(.roles[]) | unique' users.json
["admin","dev","ops"]
$ jq -c 'group_by(.active) | map({(.[0].active|tostring): (map(.name))})' users.json
[{"false":["bob","eve"]},{"true":["alice","carol","dave"]}]
$ jq -c '[3,1,2] | reverse' <<< 'null'
[2,1,3]
$ jq -c 'flatten' <<< '[[1,[2,[3]]],[4]]'
[1,2,3,4]
注意:group_by 前必须先 sort_by 同一关键字(group_by 内部会排序,但官方推荐显式排序语义);对象键上的表达式需写成 {(expr): value} 括号形式。
4.7 tostring / tonumber / tojson / fromjson
$ jq -n '42 | tostring, (42|tostring|tonumber)'
"42"
42
$ echo '"{\"a\":1}"' | jq 'fromjson'
{
"a": 1
}
$ jq -n '{a:1} | tojson'
"{\"a\":1}"
4.8 对象与数组构造、键简写
$ jq -c '[.[] | {name, age}]' users.json | head -2
[{"name":"alice","age":30},{"name":"bob","age":25},{"name":"carol","age":35},{"name":"dave","age":28},{"name":"eve","age":41}]
$ jq -c '{adults: [.[] | select(.age >= 30) | .name], count: length}' users.json
{"adults":["alice","carol","eve"],"count":5}
4.9 更新赋值:|=、=、+=
|= 对路径就地更新,= 直接赋值,+= 等复合赋值会在字段不存在时自动创建:
$ jq -c '.timeout |= . + 10' config.json | grep -o '"timeout":[0-9]*'
"timeout":40
$ jq -c '.log.level = "debug"' config.json | grep -o '"level":"[a-z]*"'
"level":"debug"
$ jq -c '.log.retention += 7' config.json | grep -o '"retention":[0-9]*'
"retention":7
$ jq -c '.server.tls.enabled = false' config.json | grep -o '"enabled":false'
"enabled":false
实测语义差异:(.enabled |= false) 中路径 .enabled 在根对象不存在时会被新建(输出尾部多出 "enabled":false),这是 |= 依据路径表达式求值的正常行为;要递归更新已有字段用 ..:
$ jq -c '(.. | .enabled?) |= false' config.json
{"server":{"host":"0.0.0.0","port":8080,"tls":{"enabled":false,"cert":"/etc/ssl/server.pem"},"enabled":false},"log":{"level":"info","file":"/var/log/app.log","enabled":false},"features":["cache","metrics","tracing"],"timeout":30,"debug":false,"comment":{"enabled":false},"enabled":false}
可见 .. 会触及所有层级(含原本没有该键的对象,同样会被补键)。若只想更新已存在的布尔键,先 select 再更新,或用 walk:
$ jq -c 'walk(if type == "string" then ascii_upcase else . end)' <<< '{"features":["cache","metrics"]}'
{"features":["CACHE","METRICS"]}
4.10 del 删除
$ jq -c 'del(.comment, .debug)' config.json
{"server":{"host":"0.0.0.0","port":8080,"tls":{"enabled":true,"cert":"/etc/ssl/server.pem"}},"log":{"level":"info","file":"/var/log/app.log"},"features":["cache","metrics","tracing"],"timeout":30}
$ jq -c 'del(.[1]) | map(.name) | join(",")' users.json
"alice,carol,dave,eve"
4.11 to_entries / from_entries / with_entries(实测 jq 1.6 别名差异)
$ jq -c 'to_entries | .[0:2]' config.json
[{"key":"server","value":{"host":"0.0.0.0","port":8080,"tls":{"enabled":true,"cert":"/etc/ssl/server.pem"}}},{"key":"log","value":{"level":"info","file":"/var/log/app.log"}}]
$ jq -n '[{key:"a", value:1},{key:"b", value:2}] | from_entries'
{
"a": 1,
"b": 2
}
$ jq -n '[{name:"x", value:true}] | from_entries'
{
"x": true
}
$ jq -c 'with_entries(select(.value | type == "number"))' config.json
{"timeout":30}
实测发现(jq 1.6):from_entries 接受 key/value 与 name/value 键名,但不接受 k/v 简写,会报错:
$ jq -n '[{k:"x", v:true}] | from_entries'
jq: error (at <unknown>): Cannot use null (null) as object key
$ jq -n '[{K:"x", V:true}] | from_entries'
jq: error (at <unknown>): Cannot use null (null) as object key
键为 null 同样报该错。需要兼容时统一改成 key/value 或先 map({key: .k, value: .v})。
4.12 if / then / elif / else / end
$ jq -r '.[] | if .score >= 90 then .name + " A" elif .score >= 80 then .name + " B" else .name + " C" end' users.json
alice A
bob C
carol B
dave A
eve C
4.13 try / catch
$ jq -n 'try error("boom") catch .'
"boom"
$ jq -n 'try ("abc" | tonumber) catch ("parse failed: " + .)'
"parse failed: Invalid numeric literal at EOF at line 1, column 3 (while parsing 'abc')"
catch 捕获到的 . 是错误消息字符串;f? 等价于 try f(吞掉错误输出空)。
4.14 reduce / foreach / limit
$ jq -n '[1,2,3,4,5] | reduce .[] as $x (0; . + $x)'
15
$ jq -n '[range(5)] | foreach .[] as $x (0; . + $x; [$x, .])'
[
[0, 0],
[1, 1],
[2, 3],
[3, 6],
[4, 10]
]
$ jq -n '[limit(3; repeat("x"))]'
[
"x",
"x",
"x"
]
reduce 只保留最终累加值;foreach 的第三个表达式在每步后产生输出;limit 截断无限生成器(如 repeat)。
4.15 字符串插值与字符串函数
$ jq -r '.[] | "\(.name) is \(.age) years old (score=\(.score))"' users.json | head -3
alice is 30 years old (score=91.5)
bob is 25 years old (score=78)
carol is 35 years old (score=88.3)
$ jq -rn '"a,b,c" | split(",") | join("-")'
a-b-c
$ jq -n '" padded " | "[" + ltrimstr(" ") + "]", "[" + rtrimstr(" ") + "]"'
"[ padded ]"
"[ padded ]"
$ jq -rn '"prefix-value" | ltrimstr("prefix-"), "nomatch" | ltrimstr("prefix-")'
"value"
"nomatch"
实测行为:ltrimstr/rtrimstr 只移除一次前缀/后缀,不匹配时原样返回;要清空两侧空白用 gsub("^ +| +$"; "")。
$ jq -rn '"report.pdf" | endswith(".pdf"), "alice" | ascii_upcase'
true
ALICE
$ printf 'hello world' | jq -R 'gsub("o"; "0")'
"hell0 w0rld"
$ printf 'path/to/file.md' | jq -R 'sub("path/"; "")'
"to/file.md"
$ printf 'a-b-c' | jq -R 'sub("(?<a>^.)-(?<b>.)$"; "\(.b)-\(.a)")'
"a-b-c"
$ printf 'user=alice id=42' | jq -R 'capture("user=(?<user>\\w+) id=(?<id>\\d+)")'
{
"user": "alice",
"id": "42"
}
$ printf 'a,b,c' | jq -R 'splits(",")'
"a"
"b"
"c"
$ printf 'ABC' | jq -R 'ascii_downcase'
"abc"
$ printf 'Hello123' | jq -R 'test("[A-Za-z]+[0-9]+")'
true
正则基于 Oniguruma(与 PCRE 语法接近),test/match 第二参数可传 "i"(忽略大小写)等标志字符串。
4.16 格式化字符串:@base64 / @csv / @tsv / @json / @sh / @uri / @text
$ jq -rn '"hello world" | @base64'
aGVsbG8gd29ybGQ=
$ jq -rn '"hello world" | @base64 | @base64d'
hello world
$ jq -rn '"a b&c" | @uri'
a%20b%26c
$ jq -cn '{a:1} | @json'
"{\"a\":1}"
$ jq -rn '["x,y", "z\"w"] | @csv'
"x,y","z""w"
$ jq -rn '["tab col", "second"] | @tsv'
tab col second
$ jq -rn '["it'"'"'s"] | @sh'
'it'\''s'
$ jq -rn '123 | @text'
123
4.17 日期与环境
$ jq -n 'now | todate'
"2026-10-17T02:49:53Z"
$ jq -n 'now | gmtime | strftime("%Y-%m-%d %H:%M:%S UTC")'
"2026-10-17 02:49:53 UTC"
$ jq -n '"2026-10-17T00:00:00Z" | fromdate'
1792195200
$ jq -rn 'env.HOME, ($ENV.PATH | split(":") | length)'
/root
6
4.18 __loc__ 与多输入 input/inputs
$ jq -n '$__loc__'
{
"file": "<top-level>",
"line": 1
}
input 消费下一个输入值,inputs 产生剩余全部输入:
$ jq -c 'input' stream.jsonl users.json
{"ts":"2026-10-17T09:00:02Z","level":"ERROR","msg":"db timeout","latency_ms":3001}
{"ts":"2026-10-17T09:00:04Z","level":"WARN","msg":"slow query","latency_ms":850}
$ jq -s -c '[inputs] | length' users.json stream.jsonl
0
第一个输出说明:filter 对输入流逐值求值,每次 input 跳取"下一个"值;-s 模式下全部输入已被 slurp,inputs 自然为空(输出 0)。
五、实战示例
5.1 JSON Lines 日志按级别统计
$ jq -s 'group_by(.level) | map({level: .[0].level, n: length, avg: ((map(.latency_ms) | add) / length | floor)})' stream.jsonl
[
{
"level": "ERROR",
"n": 2,
"avg": 1503
},
{
"level": "INFO",
"n": 2,
"avg": 10
},
{
"level": "WARN",
"n": 1,
"avg": 850
}
]
5.2 数组转 TSV 表格(给 awk/cut 下游)
$ jq -r '.[] | [.name, (.age|tostring), (.score|tostring)] | @tsv' users.json
alice 30 91.5
bob 25 78
carol 35 88.3
dave 28 95.1
eve 41 66.8
5.3 条件筛选 + 就地更新后写回文件
$ jq '(.server.port) |= 9090 | .log.level |= "warn"' config.json > config.new.json && jq '.server.port, .log.level' config.new.json
9090
"warn"
5.4 与 shell 变量交换数据
$ name=$(jq -r '.[0].name' users.json); echo "first user: $name"
first user: alice
$ jq -n --arg name "$name" '{greeting: ("hello, " + $name)}'
{
"greeting": "hello, alice"
}
5.5 校验 JSON 合法性(脚本守卫)
$ jq empty config.json && echo valid
valid
$ echo '{bad json' | jq empty >/dev/null 2>&1; echo "rc=$?"
rc=4
$ jq '.' /path/notexist.json 2>/dev/null; echo "rc=$?"
rc=2
exit code 实测汇总:正常 0;-e 末值为 false/null 为 1、无输出为 4;文件打不开为 2;JSON 解析失败为 4(jq 1.6 实测值)。
六、注意事项与常见问题
- 数字精度:jq 内部用 IEEE754 双精度。
jq -n '0.1 + 0.2'输出0.30000000000000004;超长大整数会失真,jq -n '100000000000000000000'输出1e+20。金额、ID 等需要精确表示时按字符串处理。 from_entries键名别名:jq 1.6 实测仅支持key/value、name/value;k/v、K/V会报Cannot use null (null) as object key。--args必须配-n:否则 jq 仍等待 stdin,空输入下静默无输出(rc=0),极易误判为过滤器写错。--slurpfile是数组套数组:文件本身为数组时变量形如[[...]],取首个元素要$u[0][0]。ltrimstr/rtrimstr只删一次:不是 trim;多空格场景用gsub。- 缺失键返回
null而非报错,这既是便利也是隐患:拼错字段名不会失败,可用-e或has()做断言。 ..递归更新会补键:( ... | .enabled?) |= false会给所有层级对象添加该键,含comment: null所在对象转为对象结构,务必确认这是期望行为。type永远有返回值:对null返回"null",是判断字段类型最稳的方式。-s与流式处理的选择:小文件用-s聚合方便;GB 级 JSON Lines 用逐行模式(默认)或--stream,避免整文件载入内存。- 退出码语义:
-e的 1(false/null)与 4(empty)区分了"判定为假"和"没有结果",写脚本时分别处理。
七、参考链接
- jq 官方手册:https://jqlang.github.io/jq/manual/
- jq 1.6 GitHub 仓库:https://github.com/jqlang/jq
- jq play 在线验证器:https://jqplay.org/
- 本机手册页:
man jq
站内相关:python3 篇