jq viewer 实战:JSON 格式化、查询与大文件流式处理指南
jq viewer 这个词我在不同场合听到过至少三种意思有人说的是jq本身把一坨压缩成一行的 JSON 重新排版成带缩进树结构的能力有人说的是jq .接分页器之后那种可以上下翻页、带颜色高亮的浏览体验还有人说的是带侧边栏、能点开折叠节点的图形化工具。这三件事底层做的事情差不多都是把结构化数据变得人能看懂但落地方式、适用场景和踩坑点完全不同。我平时排查线上接口返回、翻日志里嵌的 JSON、对着几万行配置找某个字段用的都是第一类和第二类组合起来的方案——终端里解决不上图形界面。这篇东西想讲清楚的就是这件事jq viewer 到底怎么用才能真的顺手。从环境配置那点容易被忽略的细节开始到查询语法的组合规则、在大文件里定位某一段数据的手法、流式解析的用法再到我这些年踩过的坑和对应的排查方法全部摊开说。不管你是刚学会jq .的新手还是已经能写reduce和walk的老手应该有能直接抄走的部分。文章里涉及的参数、配置、命令都能直接粘到终端里跑我会把每个选择背后的理由讲清楚而不是只丢一堆语法。1. 先说清楚 jq viewer 到底在解决什么问题1.1 原始 JSON 为什么不能直接看接口返回体、日志里内嵌的结构、配置中心的导出文件绝大多数情况下都是一行到底的压缩 JSON。几百个字符还好一旦到了几千字符、嵌套三四层用眼睛在里面找某个字段跟在没有目录的压缩包里翻文件差不多。你能看到内容确实在里面但找不到位置。更麻烦的是压缩 JSON 里的层级关系被抹平了。一个对象里套数组、数组里套对象、对象里再套一层可空字段这种结构在缩进格式下扫一眼就知道形状在单行格式下只能靠数括号匹配。人眼对括号的匹配能力很差尤其是深度超过三层之后。所以格式化输出不是一个美化的附加功能而是让数据可读的基础前提。jq .这个最简单的表达式做的是解析、重建、缩进、着色四件事它把线性字符流转成了二维的视觉结构。你在终端里看到的颜色也不是装饰——键名、字符串、数字、布尔值用不同的颜色区分扫视速度能快好几倍尤其是找null和false的时候。另外有个隐性收益格式化过程本身就是一次合法性校验。格式化成不出来说明 JSON 本身就有语法问题报错信息会告诉你大概在哪一列。这个用法比很多专门的校验工具都直接。1.2 三种形态边界别搞混实际用下来jq viewer 这个说法对应三类东西我列个表把差异摆清楚避免选错工具浪费时间。形态典型代表核心能力适合的场景明显短板命令行格式化jq .加输出参数输出可控、可管道、可脚本化接管道、批量处理、写进脚本长文档翻页体验差分页高亮浏览jq -C .接分页器有颜色、可搜索、可翻页人工查阅一次性看完不能折叠、不能交互编辑交互式折叠各类 TUI 浏览工具节点折叠、路径跳转探索陌生的大结构不便于管道和自动化结构化平铺扁平化转换工具每行一个路径赋值配合 grep 精确检索还原回 JSON 需要反向工具这四类里前两类是我日常的主力第三类只在完全陌生的大结构上偶尔用第四类用于我知道大概有个叫什么名字的字段但不知道它在哪一层这种检索场景。很多人一开始就去装图形化工具结果发现要处理的文件在远程机器上还得先传下来反而更慢。我的判断标准很简单能一次看完的用格式化直接输出看不完的接分页器完全不知道结构长什么样的才上交互式工具。大部分情况下一份几百 KB 的接口返回配上分页器和搜索比交互式工具快得多因为键盘操作路径短。1.3 我的默认组合与理由默认组合是jq负责解析和查询分页器负责呈现两者用管道串起来。这个分工的好处是各管各的查询逻辑写在表达式里可以单独测试、单独复用、单独塞进脚本呈现层随时可以换接分页器看、重定向到文件、丢给高亮器都行。分页器我选的是系统自带的那个理由不是它最好用而是任何一台机器上都有。你在自己电脑上配好的工具到了跳板机、容器里、临时开的机器上大概率没有这时候能用的只有标配。所以我养成的习惯是所有核心操作只用标配工具能跑通的方案额外工具当增强不当依赖。同理jq本身的版本差异也要留意。1.6 和 1.7 之间有几个行为变了最典型的是leaf_paths在 1.7 里被移除了得换成paths(scalars)。如果你写的脚本要在不同机器上跑这个差异会直接报错。我在自己机器上装 1.7 用新特性写给别人用的脚本就退回 1.6 兼容语法或者开头做个版本判断。2. 把查看环境搭对几个决定体验的细节2.1 安装与版本确认装法没什么好说的包管理器一把梭。真正要养成的习惯是装完立刻确认版本因为这个信息决定了你后面能用哪些语法。jq --version # jq-1.6 / jq-1.7.1版本差异里对你日常影响最大的几个点pick函数 1.7 才有用来从对象里挑字段比手写{a, b}灵活能处理嵌套路径。abs1.7 才有取绝对值。--raw-output01.7 才有用 NUL 分隔输出处理字符串里带换行的场景。leaf_paths1.7 移除换paths(scalars)。1.7 对数字字面量的保留有改进输出时更接近原始写法但计算仍然是双精度浮点涉及金额和高位 ID 的时候该当字符串处理还是得当字符串处理别指望版本升级解决精度问题。如果脚本要给不确定环境的人用最简单的兼容写法是避开这些差异点。比如需要挑字段就用{a: .a, b: .b}这种老式写法虽然啰嗦但到处能跑。源码编译安装的话记得带上--with-oniguruma否则正则相关的函数test、match、capture、sub会全部不可用。这个坑很隐蔽因为大部分基础语法都正常只有到用正则的时候才报function not defined。2.2 颜色为什么莫名其妙没了很多人第一次遇到的问题是明明在终端里敲jq . file.json是有颜色的一旦接上管道或者重定向到文件颜色就没了。这不是 bug是设计——jq检测到标准输出不是终端时会自动关闭颜色避免把 ANSI 转义序列写进文件。问题是接分页器的时候管道下游确实是分页器而不是终端所以颜色自动关了你在分页器里看到的是一片白。解决办法是显式强制开启jq -C . file.json | less -R-C强制着色-M强制单色两个参数干的是相反的事。分页器那边必须加-R意思是识别并保留 ANSI 转义序列不加的话你会看到一堆ESC[1;39m这样的乱码字符混在内容里。配色本身可以改用环境变量控制export JQ_COLORS1;30:0;39:0;39:0;39:0;32:1;39:1;39这个字符串是 8 段用冒号分隔的 ANSI 颜色片段顺序固定null、false、true、数字、字符串、数组、对象、对象键名。想改哪个改哪段比如把字符串改成青色就是第 5 段换成0;36。这个变量写在 shell 配置文件里全局生效比每次敲参数省事。还有个小细节如果中文内容显示成乱码大概率是分页器的字符集没设对。补一个export LESSCHARSETutf-8基本能解决。这个变量的默认值在某些老系统上不是 utf-8处理非 ASCII 内容就会出问题。2.3 把常用操作封成壳函数jq -C . f.json | less -R这条命令我一天要敲几十遍封成函数是必然的。但封装的时候有几个细节要注意不然封出来的东西反而难用。jv() { if [ -t 0 ]; then jq -C . $ | less -FR else jq -C . $ | less -FR fi }分页器参数我固定用-FR。-F是内容不足一屏时直接输出并退出不用再按q这个对小文件体验提升很大——不然你看个三行 JSON 还得退出一次。-R前面说过保留颜色。这里不建议加-X。-X的作用是不清屏看起来能让内容留在终端上方便回看但它会导致分页器不重置终端状态某些情况下退出后终端行为会变得奇怪比如命令行回显异常。真要回看内容不如直接重定向到文件再打开。如果机器上装了带语法高亮的cat替代品还可以走另一条路jq . file.json | bat -l json --pagingalways高亮器的好处是对 JSON 的着色粒度更细不好处是多一层依赖。我在自己机器上用它跳到别的机器就退化回前一个方案。还有一种情况是把查询结果直接存成文件供别人看jq --indent 4 -S . input.json output.json-S是按键名排序输出这个在对比两份文件差异的时候特别有用。不排序的话键的顺序取决于原始文件两份内容相同但键序不同的 JSON用diff比出来全是差异纯浪费时间。排序后再比较是处理配置文件的常规操作。3. 查询语法看懂结构才查得动3.1 点号、路径与可选操作符jq的表达式核心就是路径导航。.是当前输入.a取对象里名为 a 的字段.a.b连取两层.[0]取数组第一个元素.[]展开数组所有元素。这些都是入门内容但有两个细节经常被忽略。第一个是键名带特殊字符的处理。键名里有空格、连字符、点号的时候点号语法会歧义得用中括号加引号jq .[user-name] file.json jq .[a.b.c] file.json第二种写法尤其重要很多人看到键名里有点就条件反射写.a.b.c结果jq理解成三层嵌套返回null而不是报错排查起来很费时间。第二个是可选操作符?。当你不确定某一层是不是存在、是不是 null 的时候.a.b?会在.a为 null 或不是对象时静默返回null而不是抛错。这个在实际数据里非常有用因为真实接口返回的字段可空太常见了。不过?不能滥用。它把错误吞掉了有时候你其实想知道这里为什么取不到加了?就永远看不到原因。我的习惯是调试阶段不加跑通了要固化的时候再加。先把错误暴露出来确认数据结构再补上容错。3.2 遍历、切片与筛选的组合数组处理的三板斧是展开、切片、筛选。jq .items[] file.json # 展开所有元素 jq .items[0:3] file.json # 前三个 jq .items[-2:] file.json # 最后两个 jq .items[] | select(.status ok) file.json # 按条件筛.[]和.[]?的区别在于前者遇到 null 或非数组会报错后者静默跳过。处理不确定的数据源时用后者更稳。select是筛选的核心配合比较运算符用。需要注意的是**select返回的是原元素本身不是布尔值**所以select(...)之后还能继续接路径。这个特性让链式筛选很自然jq .items[] | select(.type error) | .message file.json对于嵌套数组map比手动遍历好看jq .items | map(.name) file.json jq .items | map(select(.score 80)) | length file.jsonmap(f)等价于[.[] | f]理解这个等价关系之后就能自己推导出很多写法。想统计符合条件的元素数量直接map(条件) | length比reduce好写。还有一组常用的统计函数sort_by、group_by、unique_by、min_by、max_by。它们的参数都是筛选表达式而不是字段路径字符串这点跟某些语言不一样jq sort_by(.timestamp) | last file.json jq group_by(.type) | map({type: .[0].type, count: length}) file.jsongroup_by要求先排序它内部会自己排一次但如果数据量大、本来就有序先sort_by再group_by语义更清楚。3.3 管道、逗号、括号的优先级陷阱这是jq里最容易写错的地方也是我见过最多人卡住的地方。,是分流同一个输入喂给两边两边的结果依次输出。|是接力左边的输出作为右边的输入。|的优先级高于,所以jq .a, .b | length file.json实际执行的是.a, (.b | length)不是(.a, .b) | length。想让两边都过同一个处理必须加括号jq (.a, .b) | length file.json这个优先级差异在你写复杂表达式的时候会造成非常隐蔽的错误因为结果不会报错只是数值不对。我的习惯是只要表达式里同时出现,和|一律加括号哪怕当前优先级正好对写清楚也比赌对的强。另一个常见的组合是构造新对象jq {name: .user.name, age: .user.age} file.json jq {name, age} file.json # 键名和变量同名可简写 jq {name: .user.name} {id: .user.id} file.json简写形式只对值就是一个字段路径且末段与键名相同的情况有效稍微绕一点就必须写全。新手经常在这里出错以为{a: .x.a}能简写。数组构造同理外面套一对方括号就是把多个输出收成一个数组jq [.items[] | .name] file.json这个写法很常用注意别和map混淆两者等价但后者更短。3.4 输出形态参数决定结果能不能直接用查询写对了输出格式不对结果还是要二次加工。这几个参数我几乎每次都要用到一个。参数作用典型用途-r字符串去掉外层引号提取 ID 列表喂给下一个命令-c紧凑单行输出每行一条记录便于行处理--indent n缩进宽度最大 7生成可读配置文件--tab用制表符缩进特定格式要求的场景-S键名排序文件对比场景必备-a非 ASCII 转义成\uXXXX输出到不支持 UTF-8 的通道-j结尾不加换行拼接输出-s全部输入读成一个数组需要跨行聚合时-n不读输入纯计算或构造数据-e按结果决定退出码脚本里做存在性判断-r是最常被低估的一个。不加它提取出来的字符串带着双引号直接喂给下一个命令会出错jq -r .items[].id file.json | xargs -n1 echo去掉引号才能被下游正确解析。但要注意-r只对字符串有效对象和数组还是原样输出。-c配合-r是处理日志类数据的标准组合输出成一行一条每行可以单独 grep、单独切字段。-e的用法稍微绕结果不是null也不是false时返回 0否则返回 1。在脚本里判断这个字段到底存不存在很方便if jq -e .error file.json /dev/null; then echo 有错误 fi这里有个坑空数组和空对象算真值退出码是 0。只有null和false才算假。判断字段是否存在要用has(error)而不是直接取。4. 在大结构里定位数据的手法4.1 用路径扫描摸清结构拿到一份完全陌生的 JSON第一步不是急着查字段而是先看清它的形状。paths就是干这个的。jq [paths] | length file.json # 总路径数 jq paths file.json # 所有路径每行一个数组 jq [paths] | .[0:20] file.json # 只看前 20 条paths输出的是数组形式比如[items, 0, name]。这样一眼就能看出层级深度和键名分布。数据量大的话先看数量再看样本别一次全打出来。只看叶子节点更有用因为中间节点都是容器信息量低jq [paths(scalars)] file.json jq [paths(scalars)] | .[0:30] file.json这行命令能直接告诉你这份数据里到底有哪些具体的值比翻文档快。注意 1.7 里leaf_paths没了用paths(scalars)替代语义一样。想看某个键名出现在哪些路径下jq -r paths(scalars) | select(.[-1] id) | join(.) file.jsonjoin(.)把路径数组拼成点号形式输出更接近人类习惯。4.2 按键名或值反向查找知道字段名但不知道它在哪一层是最常见的场景。这时候用递归下降..配合筛选jq .. | objects | select(has(requestId)) file.json jq .. | objects | select(has(requestId)) | .requestId file.json..会递归遍历所有节点objects过滤出对象select(has(...))挑出含目标键名的。这个写法的缺点是结果可能重复同一个对象被多层递归命中需要去重的话加| unique。按值反查稍微麻烦一点因为得先确定值的类型jq .. | strings | select(contains(timeout)) file.json jq -r paths(strings) as $p | getpath($p) | select(test(timeout)) | $p | join(.) file.json第二种写法会直接告诉你哪个路径上的值匹配了比只知道值更有用。getpath和setpath、delpaths这三个函数配合路径数组使用做局部修改很方便jq setpath([config, timeout]; 30) file.json jq delpaths([[debug], [temp]]) file.jsonsetpath的好处是路径不存在时会自动创建中间层级不用手动判断。4.3 把 jq 和 grep 的边界划清楚很多人习惯直接对原始 JSON 文件 grep这个做法在压缩单行文件上完全失效——整份文件就一行grep 命中的是整行什么信息都提取不到。正确姿势是先展开成一行一条再 grepjq -c .items[] file.json | grep -i timeout这样命中的是一整条记录能看清楚上下文。如果想要更精确就先把关键字段摘出来再匹配jq -c .items[] | {id, msg} file.json | grep -i error反过来如果需要的是哪些元素包含某个字段而不是值里含某个字符串就老老实实用jq的has、test别绕到 grep 上去。grep 负责文本匹配jq 负责结构查询两者混用要先想清楚当前在文本层还是结构层。还有一个实用技巧是把扁平化工具当可 grep 的 JSON用。这类工具把嵌套结构展开成每行一个路径赋值形式大概是json.items[0].name xxx;。展开之后随便 grep命中行本身就是完整路径信息密度很高。想改完再转回 JSON用它的反向模式即可。这个流程适合定位 人工修改的场景不适合写进自动化脚本。5. 大文件与特殊数据源的处理5.1 流式解析不把整个文件读进内存jq默认的读取方式是把整个输入解析成一棵树再执行查询。文件几十 MB 的时候内存占用会很明显几百 MB 的时候直接可能被系统杀掉。--stream参数改变的就是这个行为边解析边输出每个叶子节点输出成[路径, 值]的形式内存占用基本恒定。jq --stream select(length 2) | .[0] bigfile.jsonlength 2是过滤掉数组结束标记那种只有路径没有值的输出。这条命令会打印所有叶子节点的路径很适合先摸底。想还原成结构用fromstream配合truncate_streamjq -n fromstream(1 | truncate_stream(inputs)) --stream这个 idiom 的用途是逐个还原顶层数组元素等价于对大文件做了逐条处理但不需要一次性装载。我第一次用的时候没搞懂truncate_stream里的1是什么意思后来想明白了它把路径的第一段剥掉剥离的深度就是那个数字1表示从第一层开始重建这样一个顶层元素一条地输出。流式模式的限制也很明显没有全局视野。做聚合、排序、跨元素比较都没法直接用只能配合reduce自己累积状态。判断标准很简单——如果你的查询里有length、sort_by、group_by这类需要全量数据的操作就别指望流式老老实实分段处理。5.2 一行一条的数据源日志类文件通常是每行一个独立 JSON这种格式不适合整体解析——整份文件不是一个合法 JSONjq .会直接报解析错误。正确做法是利用jq默认的逐个读取输入行为不加-s的时候它对每行输入分别执行表达式。jq -c select(.level error) app.log jq -r [.ts, .level, .msg] | tsv app.log第二条命令把每条记录转成制表符分隔直接可以用表格软件打开或者喂给column -t对齐显示。tsv和csv这两个格式化器在处理日志时非常实用。如果确实需要跨行聚合再加-sjq -s group_by(.level) | map({level: .[0].level, n: length}) app.log但要注意-s会把所有行读成一个数组内存占用和行数成正比。几十万行的时候要谨慎。还有一种情况是文件本身是合法 JSON但每行是一个独立对象——比如某些导出的数据集。这时候-s反而更合适因为不加的话会被当成多个独立输入依次处理结果是一串输出而不是一个数组。5.3 数字精度和字符编码的坑这两个坑不常见但踩上了很难排查。数字精度方面jq内部用双精度浮点表示数字超过 2^53 的整数会丢精度。如果你的数据里有长整型 ID、订单号、时间戳微秒值解析出来末几位可能就变了。这类值一律当字符串处理不要试图在jq里做算术运算。判断标准是这个数字你会不会拿来做加减乘除会那它就该是字符串。字符编码方面输出到某些通道时如果出现\uXXXX而不是原始中文说明被自动转义了。加-a是强制转义不加则默认输出原文。反过来如果下游不支持 UTF-8主动加-a更安全。处理二进制内容比如 Base64 编码的附件时base64和base64d这对格式化器能省掉一次外部调用jq -r .payload | base64d file.json不过要注意解码出来的内容如果是二进制终端会显示成乱码得重定向到文件而不是直接打印。6. 常见报错与排查记录6.1 报错信息速查jq的报错信息其实挺直白只是第一次看不太理解。我整理了一张对照表基本覆盖日常 90% 的情况。报错信息含义处理方式Cannot index string with number把字符串当数组索引了检查上一层的.foo是不是取到了字符串而非数组Cannot iterate over null对 null 用了.[]加?或用// []兜底Cannot iterate over string对字符串用了.[]同上先确认数据结构null (null) has no keys对 null 用了keys用// {}兜底parse error: Invalid numeric literal输入不是合法 JSON检查是否存在多余字符、单引号、注释Expected value but found ...同上语法错误用格式化确认原文合法性object is not valid in a csv rowcsv收到嵌套对象先展开成扁平数组function not defined版本不支持的函数确认版本或换等价写法Cannot iterate over null这个报错最常见几乎都是字段存在但值是 null、字段压根不存在导致取到 null这两种情况。判断方法jq has(items) file.json # 字段存不存在 jq .items null file.json # 值是不是 null jq .items | type file.json # 实际类型三个一把梭问题立刻清楚。比起在报错面前猜花三秒确认类型效率高得多。6.2 输出被截断或者终端卡住分页器默认会用智能折行对超长的 JSON 行比如一个字段里塞了几千字处理得很难看横向滚动也能用但体验糟。加-S参数关闭折行用左右方向键横移反而更清楚。另一个常见现象是输出到一半卡住不动。如果输入来自某个持续写入的流比如tail -fjq默认会缓冲输出看起来就像卡住了。加--unbuffered关掉缓冲tail -f app.log | jq --unbuffered -c .这个参数对流式监控场景基本是必需的不加的话你可能要等缓冲区满了才看到内容。6.3 内存吃满和跑得慢三个原因按常见程度排用了-s读超大文件、用了需要全量的操作sort_by、group_by、unique、输入本身嵌套太深。对第一个改成流式或者分段处理。对第二个如果只是想要第一条或任意一条用first或limit提前终止jq first(.items[] | select(.ok)) file.json jq [limit(10; .items[] | select(.ok))] file.jsonfirst和limit的关键作用是短路——找到就停不会把剩余元素全跑一遍。在大数组上这个差别可能是几秒和几十秒。对第三个深嵌套结构用递归下降..会非常慢因为要对每个节点都尝试匹配。这时候用paths配合精确条件或者先定位到具体子树再操作能快一个数量级。顺带说一个经验先在样本上验证表达式再放到全量上跑。取前几条试跑jq .items[0:3] file.json | jq 你的表达式表达式写错了在小样本上立刻暴露全量跑可能几分钟才出结果白等。7. 工具横评什么时候该换个东西7.1 各方案的能力对照jq不是唯一选择但它是最通用的。其他工具各有各的甜点区我把常用几个的定位理了一下。工具类型语法/操作特点强项弱项什么时候用标准jq表达式式功能全生态最广、几乎必装大文件内存压力、学习曲线默认选择兼容实现 A语法基本兼容性能更好速度、YAML 输入支持少数内置函数缺失大文件、多格式混合兼容实现 B语法子集性能极佳启动快、内存省语法差异需要适配流水线高频调用交互式浏览器键盘导航、节点折叠探索陌生结构无法管道、无法脚本化只有人看、看完就走的场景扁平化转换每行一个路径赋值可 grep、可 diff需要反向转换定位 人工修改网页 playg round浏览器端即时反馈教学、分享表达式数据要粘贴上去调试单条表达式兼容实现指的是那些重新实现了jq语法的工具共同优势是性能共同风险是语法不完全等价。我在生产脚本里只敢用标准jq兼容实现放在交互式场景里用。原因很简单脚本出错要排查半天交互式工具出问题换个命令就行。交互式浏览器的价值主要在完全不了解结构这个阶段。打开就能看到树形结构能展开折叠能搜索路径比在终端里反复敲paths直观。但它的成本是要装远程机器上通常没有。所以我的实际使用频率远低于jq加分页器的组合。7.2 组合套路比单点工具更重要工具选型这件事我的观点是别追求单一最优追求组合顺畅。几个我常用的组合第一套是格式化 分页 搜索用来人工查阅jq -C . file.json | less -R # 进入后在 less 里按 / 搜索n 跳下一个第二套是筛选 紧凑输出 行处理用来批量提取jq -c .items[] | {id, ts} file.json | grep -v ts:null | head -20第三套是路径扫描 精确取值用来定位陌生字段jq -r paths(scalars) | select(.[-1] target) | join(.) file.json jq -r getpath([a, b, target]) file.json第四套是流式 逐条还原用来处理超出内存的文件jq --stream select(length 2) bigfile.json | head -1000 jq -n fromstream(1 | truncate_stream(inputs)) bigfile.json这四套覆盖了我日常 95% 的场景。剩下的 5% 是些奇怪的数据源YAML 配置、带注释的 JSON、超大单值那些只能看情况临时处理。8. 几个只有实际用起来才知道的细节写到这里剩下的是些零散但确实省时间的经验凑不成体系但每一条都救过我。第一条表达式一定要先在小样本上跑通。我现在的习惯是任何超过五个字符的查询都先.[0:2]取两条试一下。表达式里少个括号、多个点在小样本上一秒暴露在全量数据上可能要等几分钟甚至把内存撑爆。第二条//的兜底比?更好用。遇到可能为 null 的字段.items // []比.items[]?语义更明确——前者是默认空数组后者是跳过错误。展开数组的时候前者更安全因为你明确知道拿到的是数组而不是 null。第三条注意--arg传进来的值永远是字符串。想让参数当数字用必须用--argjson否则比较运算会得到意想不到的结果。我在这上面浪费过时间jq --argjson n 10 .items[n] file.json # n 是数字 jq --arg n 10 .items[$n] file.json # n 是字符串索引会失败字符串10当数组索引用在某些实现里会报错某些会静默返回 null特别难查。第四条构造对象时键名的引号不是必须但值里的字段路径不能省。{a: .b}里a是字面键名.b是表达式写成{a: b}会去引用一个叫b的变量不存在的变量会直接报错。这个错误在看别人代码的时候特别容易看漏。第五条unique只对排序后的数组有效。它对相邻元素去重不排序的话重复元素不相邻就不会被去掉。要么先sort再unique要么直接用unique_by(.key)这个内部会排序。第六条处理时间戳时注意单位。很多接口返回的是秒级时间戳jq的日期函数接受的是秒但有些数据源是毫秒。差一千倍的错误特别隐蔽因为结果看起来像个日期只是年份不对。转换之前先确认长度10 位是秒13 位是毫秒。第七条-r输出多行字符串时会破坏行结构。如果一个字符串值里本身含换行-r输出后会被拆成多行下游按行处理就错位了。这种场景要用--raw-output01.7 起配合 NUL 分隔或者在jq里先转义。第八条别在jq里做复杂计算。它能做但不擅长。字符串拆分、正则替换、条件嵌套堆到十几层的时候可读性和性能都会崩。遇到这种情况我一般拆成两步jq负责结构提取文本处理交给专门的命令。流水线的每一段做自己最擅长的事维护成本低得多。第九条把常用表达式存成文件。复杂的jq表达式写在命令行里转义麻烦、复用困难。放到.jq文件里用-f加载可以带注释、可以分函数维护起来舒服很多jq -f extract.jq file.json文件里可以定义函数、写多行逻辑比在命令行里塞一行强太多。团队里共享查询逻辑也走这个方式。第十条输出重定向到文件时记得用-S。前面说过键名排序这里再强调一次因为它只在你需要对比的时候才暴露出价值。养成习惯之后输出的文件天然可比省掉以后临时排序的麻烦。说到底jq viewer这件事的核心不在于记住多少函数而在于建立一套稳定的工作流先用格式化看清结构再用路径扫描定位目标然后用精确查询提取最后选对输出参数让结果能直接进下游。环境配置那部分看着琐碎——强制着色、分页器参数、字符集变量——但恰恰是这些细节决定了一天敲几十遍的命令是顺手还是别扭。我在换了新机器之后第一件事就是把这几个环境变量和壳函数配好配完之后效率差距立竿见影。至于那些更花哨的图形化工具和交互式浏览器用得上就用用不上也不必强求终端里这套组合已经能解决绝大多数问题了。