☰
Miller 分隔符完全指南:记录、字段与键值对分隔符(RS/FS/PS)的配置与实战
2026/9/25 6:02:49 网站建设 项目流程
  • CLI
  • 数据分析

【免费下载链接】miller

Miller 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 把文本数据切分为"记录、字段、键值对"三层,并分别用记录分隔符(RS)、字段分隔符(FS)与键值对分隔符(PS)来控制切分与拼接。本文以 Miller 官方参考文档 docs/src/reference-main-separators.md 为主体,完整讲解--irs/--ors、--ifs/--ofs、--ips/--ops及--rs/--fs/--ps、--repifs、--ifs-regex/--ips-regex等全套分隔符命令行参数,并对照仓库源码 pkg/cli/separators.go 与 pkg/cli/option_parse.go 讲清其底层实现。读完本文,你将能够在 DKVP、CSV、TSV、NIDX、XTAB 等任意格式之间自由切换分隔符,实现数据格式的无缝转换。

记录分隔符、字段分隔符与键值对分隔符

Miller 从三个层次来理解"分隔符":记录分隔符(record separator,RS)把一条条记录分开;字段分隔符(field separator,FS)把一条记录里的各个字段(即键值对)分开;键值对分隔符(pair separator,PS)把每个字段内部的键与值分开。

以经典的 DKVP 键值对格式 为例,数据文件 docs/src/data/a.dkvp 内容如下:

cat docs/src/data/a.dkvp
a=1,b=2,c=3 a=4,b=5,c=6

在这两行数据中:

  • 记录分隔符是换行符——它把a=1,b=2,c=3与a=4,b=5,c=6两条记录分开;
  • 字段分隔符是,——它把a=1、b=2、c=3这三个键值对字段分开;
  • 键值对分隔符是=——它把每个字段内的键(如a)与值(如1)分开。

以上是 Miller 的默认值,可通过--ips、--ops等 flag 覆盖(详见下文)。

需要特别说明的是,并非所有文件格式都同时具备这三类分隔符。例如 CSV 就没有键值对分隔符——因为 CSV 的键(表头)独占一行,值独占一行,键和值并不相邻。

同样,分隔符也并非对所有文件格式都可编程。例如在 JSON 对象 中,键值对分隔符是:、字段分隔符是,,写作{"a":1,"b":2,"c":3},但这两者都不可修改。即便执行mlr --json --ips : --ips '=' cat myfile.json,你也不会得到{"a"=1,"b"=2,"c"=3}——因为键值对分隔符:是 JSON 规范的一部分,Miller 不会、也不能改变它。这一点在源码中也有对应:在 pkg/cli/separators.go 的defaultPSes表中,json、yaml、dcf、recutils等格式的 PS 一律标注为"N/A"(不可变更、不可参数化)。

输入与输出分隔符:IRS/ORS、IFS/OFS、IPS/OPS

Miller 允许输入与输出使用相同的分隔符(如 CSV 输入、CSV 输出),也允许在输入与输出之间更换分隔符(如 CSV 输入、JSON 输出),从而实现数据的格式转换。

Miller 使用以下命名体系:

  • IRS与ORS:输入记录分隔符与输出记录分隔符;
  • IFS与OFS:输入字段分隔符与输出字段分隔符;
  • IPS与OPS:输入键值对分隔符与输出键值对分隔符。

例如,把输入为逗号/等号风格的 DKVP 数据,转换为输出为分号/冒号风格的数据:

mlr --ifs , --ofs ';' --ips = --ops : cut -o -f c,a,b docs/src/data/a.dkvp
c:3;a:1;b:2 c:6;a:4;b:5

这里cut -o -f c,a,b把字段顺序重排为c,a,b,同时输入侧按,与=解析,输出侧按;与:写回。

再看 CSV 场景。CSV 没有键值对分隔符,但字段分隔符同样可以自由切换:

mlr --csv head -n 2 docs/src/example.csv
color,shape,flag,k,index,quantity,rate yellow,triangle,true,1,11,43.6498,9.8870 red,square,true,2,15,79.2778,0.0130
mlr --csv --ofs pipe head -n 2 docs/src/example.csv
color|shape|flag|k|index|quantity|rate yellow|triangle|true|1|11|43.6498|9.8870 red|square|true|2|15|79.2778|0.0130

只加了一个--ofs pipe,输出字段分隔符就从逗号变成了管道符|。

双向同设的快捷 flag:--rs、--fs、--ps

如果数据的输入输出分隔符相同、不需要分别设置,可以使用--rs、--fs、--ps。例如--fs :与--ifs : --ofs :完全等价,但敲击键盘的次数更少。

以使用分号作字段分隔符、冒号作键值对分隔符的 DKVP 数据 docs/src/data/modsep.dkvp 为例:

cat docs/src/data/modsep.dkvp
a:1;b:2;c:3 a:4;b:5;c:6
mlr --fs ';' --ps : cut -o -f c,a,b docs/src/data/modsep.dkvp
c:3;a:1;b:2 c:6;a:4;b:5

从源码看,--rs、--fs、--ps的解析器会同时把值写入ReaderOptions与WriterOptions:例如 pkg/cli/option_parse.go 中--fs的解析器同时设置options.ReaderOptions.IFS与options.WriterOptions.OFS,并分别标记ifsWasSpecified与ofsWasSpecified。这就是"一个 flag 同时作用于输入与输出"的实现机制。

多字符分隔符

除少数不允许参数化的文件格式外,所有分隔符都可以是多字符的。但注意:对于 CSV(CSV-lite 没有这些限制),IRS必须是\n,且IFS必须是单字符。

例如把输入的分号/冒号数据,输出为三连分号;;;与:=风格:

mlr --ifs ';' --ips : --ofs ';;;' --ops := cut -o -f c,a,b docs/src/data/modsep.dkvp
c:=3;;;a:=1;;;b:=2 c:=6;;;a:=4;;;b:=5

输入侧照旧按单字符;与:解析,输出侧则按多字符;;;与:=拼接,充分体现了 Miller 输入/输出分隔符相互独立的设计。

--repifs:把连续重复的分隔符视为一个

如果数据中的字段分隔符是一个或多个连续空格,可以使用--ifs space --repifs来解析。更一般地,--repifs表示字段分隔符的多次连续出现只算作一次。

两种常见的处理语义对比:

  • 在 CSV 数据中,我们常用空字符串表示空值,例如2,9,,,,,6,5,4——这里每个逗号都是有效分隔,连续逗号之间是空字段,必须保留;
  • 但如果字段分隔符是空格,把2 4 5解析成与2 4 5相同往往更自然——此时--repifs --ifs ' '就能实现这个效果。

实际上,--ipprint选项在内部就是用--repifs实现的。从源码看,pkg/cli/separators.go 的defaultAllowRepeatIFSes表中pprint格式的默认值即为true,而 CSV、DKVP、TSV 等格式默认为false——也就是说 PPRINT 格式天生就允许重复分隔符(用空格对齐),其余格式则需要显式传入--repifs才会开启。

来看官方示例,数据文件 docs/src/data/extra-spaces.txt:

cat docs/src/data/extra-spaces.txt
oh say can you see by the dawn's early light what so
mlr --ifs ' ' --repifs --inidx --oxtab cat docs/src/data/extra-spaces.txt
1 oh 2 say 3 can 4 you 1 see 2 by 3 the 4 dawn's 1 early 2 light 3 what 4 so

--inidx把每条记录当作 NIDX(索引编号)格式读入——字段没有名字,自动按位置编号 1、2、3、4;--oxtab则输出为 XTAB 竖向表格。行内多个连续空格都被折叠成一个字段分隔,所以oh say can you被正确切成了 4 个字段。

正则表达式分隔符:--ifs-regex 与 --ips-regex

IFS和IPS可以是正则表达式:使用--ifs-regex或--ips-regex代替--ifs或--ips即可。

例如,--ifs space --repifs和--ifs-regex '( )+'都能实现"连续空格算一个分隔"的效果。但官方文档特别提示:--ifs space --repifs比--ifs-regex '( )+'大约快 3 倍——正则表达式功能强大,但速度更慢。因此,能用手写空格折叠解决的场景,优先用--repifs;正则分隔符更适合真正需要正则能力的复杂场景(例如按多种空白字符混合切分)。

从源码实现看,--ifs-regex的解析器(pkg/cli/option_parse.go)会调用lib.CompileMillerRegex把参数编译为正则对象,存入options.ReaderOptions.IFSRegex;--ips-regex同理存入IPSRegex。这与--ifs直接存入字符串字面量的路径不同,也正是它更慢的原因所在。

另外需要注意:spaces、tabs、whitespace这些正则别名本身已经是正则表达式,因此不应与--repifs搭配使用。事实上,当提供了--ifs-regex时,--repifs会被忽略。

分隔符别名:避免 shell 转义的烦恼

许多分隔符在 shell 中需要转义,例如--ifs ';'、--ofs '|'。Miller 为此提供了命名别名,可在命令行直接书写。执行以下命令查看完整列表:

mlr help list-separator-aliases
ascii_esc = "\x1b" ascii_etx = "\x03" ascii_fs = "\x1c" ascii_gs = "\x1d" ascii_null = "\x00" ascii_rs = "\x1e" ascii_soh = "\x01" ascii_stx = "\x02" ascii_us = "\x1f" asv_fs = "\x1f" asv_rs = "\x1e" colon = ":" comma = "," cr = "\r" crcr = "\r\r" crlf = "\r\n" crlfcrlf = "\r\n\r\n" equals = "=" lf = "\n" lflf = "\n\n" newline = "\n" pipe = "|" semicolon = ";" slash = "/" space = " " tab = "\t" usv_fs = "\xe2\x90\x9f" usv_rs = "\xe2\x90\x9e"

这些别名中,crlf、crlfcrlf、lflf等对换行风格特别有用——例如--ors crlf可以输出 RFC-4180 风格的 CRLF 行尾。asv_fs/asv_rs(对应 ASCII 单位分隔符/记录分隔符)与usv_fs/usv_rs(对应 Unicode U+241F/U+241E)则是 ASV/USV 这类专门用于分隔字段/记录的控制字符格式(见 CSV/TSV/ASV/USV 等格式说明)。

而--ifs-regex与--ips-regex则另有正则别名表:

mlr help list-separator-regex-aliases
spaces = "( )+" tabs = "(\t)+" whitespace = "([ \t])+"

从源码 pkg/cli/separators.go 看,这些别名都定义在SEPARATOR_NAMES_TO_VALUES与SEPARATOR_REGEX_NAMES_TO_VALUES两张映射表中;pkg/cli/mlrcli_util.go 中的SeparatorFromArg与SeparatorRegexFromArg函数负责把别名解析为真实的分隔符字符串——如果命中别名表就返回对应值,否则原样返回参数本身。这也是 shell 补全(pkg/cli/flag_types.go 中的FlagValueCandidates)能够为--ifs等 flag 枚举候选值的基础。

命令行 flag 汇总

综合以上内容,Miller 的分隔符相关 flag 全景如下:

--rs --irs --ors --fs --ifs --ofs --repifs --ifs-regex --ps --ips --ops --ips-regex

其中:

  • --rs {string}/--irs {string}/--ors {string}:记录分隔符(输入和输出 / 仅输入 / 仅输出);
  • --fs {string}/--ifs {string}/--ofs {string}:字段分隔符(输入和输出 / 仅输入 / 仅输出);
  • --ps {string}/--ips {string}/--ops {string}:键值对分隔符(输入和输出 / 仅输入 / 仅输出);
  • --repifs:允许输入字段分隔符连续重复出现并只算一次;
  • --ifs-regex {string}/--ips-regex {string}:以正则表达式形式指定输入字段分隔符 / 输入键值对分隔符。

完整的 flag 说明可参见 reference-main-flag-list.md 的分隔符 flag 一节,或在终端运行mlr help separator-flags查看。

DSL 内置变量:IRS、ORS、IFS、OFS、IPS、OPS

Miller 在 DSL 中向用户暴露了只读的 内置变量:IRS、ORS、IFS、OFS、IPS、OPS。与 AWK 不同,你不能在 begin 块中给这些变量赋值——它们的值只反映你在命令行指定的内容,因此用途有限(主要用于调试或在put/filter中读取当前生效的分隔符)。

例如,下面的命令在每条记录上追加一个字段d,其值由输入字段分隔符,与输出字段分隔符;拼接而成:

mlr --ifs , --ofs ';' --ips = --ops : --from docs/src/data/a.dkvp put '$d = ">>>" . IFS . "|||" . OFS . "<<<"'
a:1;b:2;c:3;d:>>>,|||;<<< a:4;b:5;c:6;d:>>>,|||;<<<

可以看到d字段中嵌入了IFS(,)与OFS(;)的真实值,直观验证了内置变量的含义。

哪些分隔符适用于哪些文件格式

不同文件格式对 RS/FS/PS 的支持程度差异很大。官方文档给出了完整的对照表:

RSFSPS
CSV默认\n;可设为\r\n*默认,;必须是单字符无
TSV默认\n;可设为\r\n*默认\t;必须是单字符无
CSV-lite默认\n*默认,无
TSV-lite默认\n*默认\t无
JSON不适用;记录位于{与}之间恒为,;不可修改恒为:;不可修改
YAML不适用;文档由---或单个数组分隔不适用;不可修改恒为:;不可修改
DCF不适用;段落由空行分隔不适用;不可修改恒为:;不可修改
recutils不适用;记录由空行分隔不适用;不可修改恒为:;不可修改
DKVP默认\n默认,默认=
DKVPX默认\n默认,;输入必须是单字符默认=;输入必须是单字符
NIDX默认\n默认空格无
XTAB不使用;记录之间用额外的 FS 分隔\n*默认:空格并允许重复
PPRINT默认\n*空格并允许重复无
Markdown恒为\n;不可修改 *一个或多个空格,加|,再加一个或多个空格;不可修改无

* 在 Windows 上为\r\n。

配套的注意事项如下:

  • CSV:IRS必须是换行符(输入时接受 CR/LF 行尾);ORS必须是换行符(默认)或回车换行——例如--ors crlf或--ors '\r\n',用于在任何平台上输出 RFC-4180 风格行尾;IFS必须是单字符。(CSV-lite 没有这些限制。)
  • TSV:IRS必须是换行符(输入时接受 CR/LF 行尾);ORS必须是换行符(默认)或回车换行;IFS必须是制表符。(TSV-lite 没有这些限制。)
  • JSON 与 YAML:忽略命令行传入的分隔符 flag。
  • 无表头 CSV与使用逗号作为IFS的 NIDX 格式有相当程度的重叠,详见 CSV 带表头与不带表头。
  • XTAB的记录分隔符是字段分隔符的重复:例如一条记录是x=1,y=2、下一条是x=3,y=4,且OFS为换行符时,输出行依次为x 1、y 2、一个额外换行、x 3、y 4。这意味着:要定制 XTAB,应设置OFS而非ORS。

这张表的底层依据同样可以在 pkg/cli/separators.go 中找到:defaultFSes、defaultPSes、defaultRSes三张表按格式(csv、tsv、csvlite、tsvlite、json、yaml、dkvp、dkvpx、nidx、xtab、pprint、markdown等)记录了每种格式的默认分隔符;凡标记为"N/A"的,即表示该格式不可参数化。例如defaultFSes["json"] = "N/A"、defaultRSes["xtab"] = "\n\n"(XTAB 用两个换行——即空行——分隔记录),与文档表格完全吻合。

小结与实战建议

Miller 的分隔符体系可以概括为三句话:

  1. 三层分隔:记录分隔符(RS)切记录、字段分隔符(FS)切字段、键值对分隔符(PS)切键值,三者分别有输入(I)与输出(O)两个版本,共六个独立可配置项。
  2. 按需取舍:DKVP、XTAB 等格式三类分隔符齐全且全部可编程;CSV/TSV 无键值对分隔符但字段分隔符可编程;JSON/YAML/DCF/recutils 等格式的分隔符由规范决定,不可修改。
  3. 用对工具:普通场景直接用--ifs/--ofs/--ips/--ops或快捷的--fs/--ps;连续空格数据用--ifs space --repifs(比正则快约 3 倍);真正需要正则切分时用--ifs-regex/--ips-regex;需要跨平台稳定行尾时用--ors crlf等别名。

掌握这些参数,你就可以在 CSV、TSV、DKVP、NIDX、XTAB、JSON 等格式之间自由转换数据,也可以在同一格式内部按需定制分隔符——这正是 Miller 作为"面向命名索引数据的 awk/sed/cut/join/sort"的核心能力之一。

  • CLI
  • 数据分析

【免费下载链接】miller

Miller 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
点击查看免费下载

相关推荐

上一篇:如何用Mousecape打造专属桌面体验:5步个性化指南
下一篇:Zeus IoT:解锁百万级设备并发的工业级物联网平台

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询