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)实测通过。

Linux命令Linux系统UbuntuUbuntu 22.04命令行2026/10/17

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传入字符串变量 $namefilter 中 $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 实测值)。

六、注意事项与常见问题

  1. 数字精度:jq 内部用 IEEE754 双精度。jq -n '0.1 + 0.2' 输出 0.30000000000000004;超长大整数会失真,jq -n '100000000000000000000' 输出 1e+20。金额、ID 等需要精确表示时按字符串处理。
  2. from_entries 键名别名:jq 1.6 实测仅支持 key/value、name/value;k/v、K/V 会报 Cannot use null (null) as object key。
  3. --args 必须配 -n:否则 jq 仍等待 stdin,空输入下静默无输出(rc=0),极易误判为过滤器写错。
  4. --slurpfile 是数组套数组:文件本身为数组时变量形如 [[...]],取首个元素要 $u[0][0]。
  5. ltrimstr/rtrimstr 只删一次:不是 trim;多空格场景用 gsub。
  6. 缺失键返回 null 而非报错,这既是便利也是隐患:拼错字段名不会失败,可用 -e 或 has() 做断言。
  7. .. 递归更新会补键:( ... | .enabled?) |= false 会给所有层级对象添加该键,含 comment: null 所在对象转为对象结构,务必确认这是期望行为。
  8. type 永远有返回值:对 null 返回 "null",是判断字段类型最稳的方式。
  9. -s 与流式处理的选择:小文件用 -s 聚合方便;GB 级 JSON Lines 用逐行模式(默认)或 --stream,避免整文件载入内存。
  10. 退出码语义:-e 的 1(false/null)与 4(empty)区分了"判定为假"和"没有结果",写脚本时分别处理。

七、参考链接

站内相关:python3 篇