gron — 把 JSON 拍平成 grep 能吃的赋值行
是什么
gron 是 Tom Hudson(GitHub 名 tomnomnom)2016 年用 Go 写的单文件命令行工具——把任意 JSON 拍平成一行一个的 JavaScript 赋值语句,让 grep / sed / awk / ripgrep 这些只懂”行”的老工具也能处理嵌套 JSON。
日常类比:
- jq:JSON 界的瑞士军刀,但你得学一门小 DSL(
.|selectmap) - gron:JSON 界的”翻译官”——它不让你学新语言,而是把 JSON 翻译成你已经会的格式(一行一条赋值),然后你用一辈子用过的 grep 就行
最小例子:
curl -s https://api.github.com/repos/tomnomnom/gron | gron | grep stargazers看输出长这样:
json.stargazers_count = 13892;json.stargazers_count 是从 JSON 根节点到目标字段的完整路径;= ...; 是 JS 风格赋值。每一行自带完整路径——所以 grep 一行就够了,不用先想”我要怎么爬到这个字段”。
为什么重要
不理解 gron,下面这些事都没法解释:
- 为什么有了 jq 还有人发明 gron——因为让工具配人比”让人配工具”省学习成本
- 为什么 14k 星的工具核心代码不到 500 行 Go——它的设计哲学是”做一件事并接好 Unix 管道”
- 为什么
gron -u(ungron)能把过滤后的若干行反拼回合法 JSON——这是 gron 真正巧妙的地方 - 为什么很多人 debug API 第一反应是
curl ... | gron | grep,而不是curl ... | jq '...'——前者零思考
核心要点
gron 的设计可以拆成 三个动作:
-
拍平(gron):把树形 JSON 变成一行一条的
path = value;。每行完整路径 + 完整值,所以行内自包含——这正是 grep 友好的关键。 -
过滤(grep / rg / sed):用任意行式工具挑出感兴趣的行。这里不用 gron 自己出力——它把过滤这件事还给了 Unix。
-
拼回(gron -u):把过滤后的若干行重新拼回合法 JSON。这一步叫 ungron,让 gron 不只是”看”工具,也能当”裁剪”工具。
三步加起来就是 gron 的全部。没有 DSL,没有插件,没有运行时配置。
实践案例
案例 1:从 GitHub API 抠两个字段
curl -s https://api.github.com/repos/tomnomnom/gron \ | gron \ | grep -E '\.(stargazers_count|open_issues) ='输出:
json.open_issues = 47;json.stargazers_count = 13892;对照 jq 写法:jq '.stargazers_count, .open_issues'——jq 更短,但你得记住 jq 语法。gron 的赌注是:grep 你已经会了。
案例 2:先 grep,再用 ungron 拼回 JSON
想从一堆数据里只保留 stargazers 字段并重新输出合法 JSON:
curl -s https://api.github.com/repos/tomnomnom/gron \ | gron \ | grep stargazers \ | gron -u输出:
{ "stargazers_count": 13892, "stargazers_url": "https://api.github.com/repos/tomnomnom/gron/stargazers"}gron -u 把”行”重新拼成”树”。这一步是 jq / yq 都没有的形态——它们不能”先用 grep 裁剪再吐回 JSON”。
案例 3:diff 两份 JSON 像 diff 两个文本文件
diff <(gron a.json) <(gron b.json)因为 gron 输出每行带完整路径且按字母排序,行级 diff 直接告诉你”json.users[3].email 变了”。jq 的 diff 远没这么直观——树形 diff 难看懂。
踩过的坑
-
数组下标用
[0]不是.0:路径里数组用json.results[0].name,新人常错写json.results.0.name,grep 不到。 -
输出体积膨胀 3-5 倍:因为每行都重复完整路径。50KB 的 JSON 拍平后可能 200KB+。流式大文件不要用 gron(它一次性读入)。
-
ungron 必须吃完整子树:grep 出
json.users[0].name但漏了json.users[0]这一行(数组本身的声明),ungron 会拼失败。最稳的做法是grep -E 'pattern|^json\.users\[0\] '把容器行也带上。 -
特殊 key 自动加方括号:
{"a-b": 1}拍平成json["a-b"] = 1;。grep 时引号要写对,不然 shell 解析报错。
适用 vs 不适用场景
适用:
- ad-hoc 看陌生 API 响应——不用先学 schema,gron 一遍 grep 就找到字段
- diff 两份 JSON 配置文件——用普通
diff就行 - shell 脚本里只想抠一两个字段,不想引入 jq 依赖
- 教零基础同学读 JSON——“你看,每一行就是一个完整赋值”,比 jq 直观
不适用:
- 复杂转换 / 聚合 / 重组结构——用 jq
- 流式大文件(>100MB)——gron 一次性读入,会爆内存
- 需要保留原 JSON 字段顺序——ungron 按字母重排
- YAML / XML / TOML——gron 只吃 JSON;YAML 用 yq
历史小故事(可跳过)
- 2016 年:Tom Hudson(安全研究员,写过 ffuf 和 assetfinder)在做漏洞赏金时被嵌套 JSON 折磨——既不想学 jq DSL,又想用 grep。他想:“那把 JSON 翻译成 grep 能吃的格式不就行了?” 一周后第一版 gron 上线。
- 2018 年:加上 ungron(
-u)。这一步把 gron 从”查看工具”升级成”管道工具”——可以双向走。 - 2024 年:14k 星,被 fzf / ripgrep 之流并列推荐为”现代 Unix 命令行套件”成员。Go 单文件,依赖少,brew/apt/pacman 都有。
学到什么
- 让工具配人,比让人配工具省事——gron 没发明新语法,它把 JSON 翻成”你已经会的格式”
- Unix 哲学的现代演绎:做一件事(拍平),接好管道(grep/sed),可逆(ungron)
- 设计的价值在于减法:gron 没 DSL、没配置、没插件,但因此它能和任何行式工具组合
- 同一个问题不一定只有一种工具:jq、yq、fx、gron 都处理 JSON,但心智模型完全不同——按场景挑
延伸阅读
- 主页:gron GitHub(README 5 分钟读完)
- 作者博客:tomnomnom.com 看他怎么思考 CLI 工具设计
- jq —— gron 的”对照组”:DSL 路线 vs 翻译路线
- yq —— 同心智模型搬到 YAML
- fx —— 交互式 TUI 路线,又一种 JSON 思路
- ripgrep —— gron 最佳搭档:
gron file.json | rg pattern
关联
- jq —— 同处理 JSON,但走 DSL 路线;gron 走”翻译成行 + 复用 grep”路线,互补不替代
- yq —— 把 jq 心智搬到 YAML;gron 把 JSON 搬到 grep 心智,两个方向不同
- fx —— 交互 TUI 看 JSON;gron 是非交互管道
- ripgrep —— gron 拍平后用 rg 比 grep 更快,二者天作之合
- ffuf —— 同作者 tomnomnom 的另一个工具,体现他”小工具 + 接管道”的一贯设计