news 2026/9/24 14:50:51

Miller 全字段操作实战:批量重命名、全局搜索替换与 `$*` 记录重排

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Miller 全字段操作实战:批量重命名、全局搜索替换与 `$*` 记录重排

Miller 全字段操作实战:批量重命名、全局搜索替换与$*记录重排

【免费下载链接】millerMiller is like awk, sed, cut, join, and sort for name-indexed data such as CSV, TSV, and tabular JSON项目地址: https://gitcode.com/gh_mirrors/mi/miller

本指南聚焦 Miller 中一类高频且易混淆的需求:不再针对单个字段名写死操作,而是批量作用于当前记录的全部字段——包括把空格批量替换为下划线、清洗带换行符的字段名、对每个字段做搜索替换、以及用映射字面量一次性重命名并重排整个记录。你将掌握两条互补的技术路线:rename动词(带-g/-r选项的正则批量重命名),以及基于 Miller 编程语言(DSL)的put脚本(配合$*$[k]gsub与 out-of-stream 变量@),并理解各自的适用场景与底层实现。

一切围绕$*:Miller 的“全字段”视角

Miller 处理的是 name-indexed 数据(如 CSV、TSV、DKVP 等),每条记录在 DSL 中是一个键值对集合。在put/filter的 DSL 语境下,三个关键符号构成了全字段操作的基础:

  • $*:当前整条记录本身,作为一个 map(映射)参与表达式运算,可整体读取、整体赋值;
  • $[k]:以计算出的键k(变量或表达式)动态索引字段值,这是“遍历所有字段”循环体的核心;
  • @name:out-of-stream 变量,跨记录持续存在,常用于累加器等需要跨行状态的场景。

因此,“对全部字段做 X”在 Miller 中通常有两种实现:一是命令行rename等动词直接提供批量语义;二是用 DSL 的for (k in $*) { ... }循环显式遍历。下面依次展开。

批量重命名字段:rename -g -r

假设列名中含有空格,希望把列名里的空格替换为下划线。示例数据 docs/src/data/spaces.csv 内容如下:

column 1,column 2,column 3 apple,ball,cat dale egg,fish,gale

最简单的做法是使用mlr rename,配合-g-r两个选项:

mlr --csv rename -g -r ' ,_' data/spaces.csv

输出:

column_1,column_2,column_3 apple,ball,cat dale egg,fish,gale

这里-r表示按正则模式匹配字段名(而不是显式逐列指定 old,new 对),-g表示在每个字段名内部做全局替换(而非只替换首次出现)。注意上例中替换模式是' ,_',即“把空格换成下划线”。

也可以换用 pprint 输出格式查看效果更直观:

mlr --csv --opprint rename -g -r ' ,_' data/spaces.csv

输出:

column_1 column_2 column_3 apple ball cat dale egg fish gale

-g-r的语义与源码实现

从源码 pkg/transformers/rename.go 可以看到这两个选项的精确定义:

  • -r:把旧字段名当作正则表达式处理。例如"ab""a.*b"会分别匹配所有含子串ab或匹配a.*b的字段名,也可用"^ab$""^a.*b$"形式的锚定;新字段名可以是普通字符串,也可以包含\1\9的捕获组。若用双引号包裹正则并在其后跟i,可表示大小写不敏感匹配(如'"name"i')。
  • -g:在每个字段名内做全局替换(等价于 Go 的ReplaceAllString),而非首次匹配替换。

源码中一个容易被忽略的细节是(见 rename.go):

if doGsub { doRegexes = true }

-g隐式开启正则模式(因为全局替换必然基于模式匹配实现)。在实际执行时(transformWithRegexes),-gregex.ReplaceAllString(oldName, replacement)对所有匹配处替换;不带-g时则走lib.RegexCompiledSub,只替换每个字段名中的首个匹配。

rename动词的参数形式是逗号分隔的old1,new1,old2,new2,...对,个数必须为偶数,否则解析器会报names string must have even length(见 rename.go)。此外-r还支持捕获组重排,例如官方用法中列出的:

mlr rename -r 'Date_([0-9]+).*,\1' # 把 Date_20151015xxx 这类字段重命名为 20151015 mlr rename -r '"name"i,Name' # 把 name/Name/NAME 等统一改为 Name

用 DSL for 循环实现同样的重命名

rename动词虽然直接,但只覆盖“改名”这一种变换。当你想在改名之外再叠加其他逻辑时,可以用put脚本遍历$*。官方示例脚本 docs/src/data/bulk-rename-for-loop.mlr 如下:

map newrec = {}; for (oldk, v in $*) { newrec[gsub(oldk, " ", "_")] = v; } $* = newrec

执行:

mlr --icsv --opprint put -f data/bulk-rename-for-loop.mlr data/spaces.csv

输出与上一种方式完全一致:

column_1 column_2 column_3 apple ball cat dale egg fish gale

这段脚本的核心思路是:先声明一个空 mapnewrec,遍历$*(键为oldk、值为v),用gsub(oldk, " ", "_")生成新键,再把原值放入newrec,最后整体赋值$* = newrec。对比rename动词,这种写法把“字段名变换”完全交给了 DSL 的字符串函数,后续可自由扩展(例如同时修改值、过滤字段等)。

处理含换行符的字段名:先清洗再赋值

上一节的方案并不足以应付字段名本身包含换行符(carriage return / line feed)的情形——CSV 字段名被引号包裹且内部含\n时,rename -r的正则对\n的处理比较棘手。此时应回到 Miller 编程语言,用put完成“取记录 → 清洗键 → 重建记录”三步。

示例数据 docs/src/data/header-lf.csv(第一个列名是"field\nA",即引号内带换行):

"field A",field B 1,2 3,3 6,6

执行:

mlr --csv --from data/header-lf.csv put ' map inrec = $*; $* = {}; for (oldkey, value in inrec) { newkey = clean_whitespace(gsub(oldkey, "\n", " ")); $[newkey] = value; } '

输出:

field A,field B 1,2 3,3 6,6

要点拆解:

  1. map inrec = $*;:先把当前记录整体快照到一个局部 map,避免在循环中边遍历边修改$*
  2. $* = {};:清空当前记录,准备重建;
  3. for (oldkey, value in inrec) { ... }:遍历快照中的每一对键值;
  4. newkey = clean_whitespace(gsub(oldkey, "\n", " "));:先用gsub把字段名里的换行符替换为空格,再用clean_whitespace收敛空白(trim 与折叠多余空白),得到干净的新键名;
  5. $[newkey] = value;:以计算出的新键写入当前记录,这正是$[k]动态下标能力的典型用法。

全字段搜索替换:$[k]动态下标

$name = gsub($name, "old", "new")推广到所有字段,本质就是上节循环思想的复用——只不过这次变换作用在上,且不需要重建记录(直接改写原键对应的值即可)。

示例数据 docs/src/data/sar.csv:

a,b,c the quick,brown fox,jumped over,the,lazy dogs

官方脚本 docs/src/data/sar.mlr 只有三行:

for (k in $*) { $[k] = gsub($[k], "e", "X"); }

执行:

mlr --csv put -f data/sar.mlr data/sar.csv

输出:

a,b,c thX quick,brown fox,jumpXd ovXr,thX,lazy dogs

这里for (k in $*)遍历的是当前记录的,循环体内$[k] = gsub($[k], "e", "X")同时用到动态读($[k]取值)与动态写($[k]赋值)。注意for (k in $*)for (k, v in $*)的差异:前者只遍历键,适合此类“键已确定、只改值”的场景,写法更简洁。

整体重命名与重排:$* = 映射字面量

借助 Miller 5.0.0 引入的 map 字面量语法与$*整体赋值,你可以完全泛化renamereorder等动词——不止改名、不止换序,甚至可以同时重排、改名、删字段、增字段,并让新字段依赖其他字段的值与跨记录的 out-of-stream 状态。

示例数据 docs/src/data/small(DKVP 格式):

a=pan,b=pan,i=1,x=0.346791,y=0.726802 a=eks,b=pan,i=2,x=0.758679,y=0.522151 a=wye,b=wye,i=3,x=0.204603,y=0.338318 a=eks,b=wye,i=4,x=0.381399,y=0.134188 a=wye,b=pan,i=5,x=0.573288,y=0.863624

执行:

mlr put ' begin { @i_cumu = 0; } @i_cumu += $i; $* = { "z": $x + y, "KEYFIELD": $a, "i": @i_cumu, "b": $b, "y": $x, "x": $y, }; ' data/small

输出:

z=0.346791,KEYFIELD=pan,i=1,b=pan,y=0.346791,x=0.726802 z=0.758679,KEYFIELD=eks,i=3,b=pan,y=0.758679,x=0.522151 z=0.204603,KEYFIELD=wye,i=6,b=wye,y=0.204603,x=0.338318 z=0.381399,KEYFIELD=eks,i=10,b=wye,y=0.381399,x=0.134188 z=0.573288,KEYFIELD=wye,i=15,b=pan,y=0.573288,x=0.863624

这个示例同时演示了四类能力:

  1. begin块初始化 out-of-stream 变量@i_cumu = 0在读取第一条记录前执行一次;
  2. 跨记录累计@i_cumu += $i把每条记录的i累加起来,因此输出中i列是 1、3、6、10、15 的累计序列;
  3. map 字面量整体重建记录$* = { ... }定义了输出记录的字段及其顺序。与reorder动词(见 reference-verbs.md 中reorder条目)相比,这里可自由决定新记录里出现哪些字段、以何种顺序出现、每个字段的取值表达式;
  4. 字段交叉引用:新字段z$x + y(注意y是未加$前缀的裸名——在 DSL 中put的表达式语境里它被视为当前记录的字段,与$y等价),而yx字段做了交换:"y": $x, "x": $y

由于$*赋值发生在@i_cumu += $i之后,新的i字段拿到的是累计值;而原i字段被彻底覆盖。这正是“全字段重命名与重排”最彻底的形态——旧记录被整体替换为一张新 map。

小结与进一步阅读

  • 纯批量改名:优先用mlr rename -g -r-g隐式开启正则模式并做全局替换,支持捕获组\1~\9"regex"i大小写不敏感写法,实现见 pkg/transformers/rename.go;其行为有对应的回归测试目录 test/cases/verb-rename-no-regex/ 与 test/cases/verb-rename-regex/ 可对照验证。
  • 改名之外还要加工:用put+for (k in $*)for (oldk, v in $*)循环,配合gsubclean_whitespace等字符串函数,动态读写$[k]
  • 整体重建记录:用 map 字面量整体赋值$* = { ... },可同时完成重命名、重排、增删字段与跨记录累计,是rename/reorder动词的 DSL 泛化形态。
  • 三种技巧覆盖了字段名清洗、全字段值替换、记录级重组三大类场景,是 Miller DSL 中复用率最高的模式;DSL 的完整语法与内置函数可继续阅读 miller-programming-language.md、reference-verbs.md 与 reference-dsl-builtin-functions.md。

【免费下载链接】millerMiller is like awk, sed, cut, join, and sort for name-indexed data such as CSV, TSV, and tabular JSON项目地址: https://gitcode.com/gh_mirrors/mi/miller

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 14:48:38

企业如何应用智能客服?5 款产品的全渠道接入方案对比与实战

当一家企业的客户同时活跃在微信公众号、小程序、官网、APP、抖音、电话等六七个渠道上时,客服团队面临的不是"要不要做智能客服"的问题,而是"怎么让一套知识库和对话引擎同时服务所有渠道、并且把会话数据统一回流到 CRM 和工单系统&quo…

作者头像 李华
网站建设 2026/9/24 14:44:45

ToastFish 完整指南:用 Windows 通知栏背单词

ToastFish 完整指南:用 Windows 通知栏背单词 【免费下载链接】ToastFish 一个利用摸鱼时间背单词的软件。 项目地址: https://gitcode.com/GitHub_Trending/to/ToastFish ToastFish 是一款开源的背单词软件,它把单词卡片通过 Windows 系统通知推…

作者头像 李华
网站建设 2026/9/24 14:41:26

Perfetto 内存分析:用 heapprofd 抓住 Android 内存泄漏

Perfetto 内存分析:用 heapprofd 抓住 Android 内存泄漏 【免费下载链接】perfetto Production-grade client-side tracing, profiling, and analysis for complex software systems. 项目地址: https://gitcode.com/GitHub_Trending/pe/perfetto 凌晨的告警…

作者头像 李华