- CLI
- 数据分析
【免费下载链接】miller
Miller is like awk, sed, cut, join, and sort for name-indexed data such as CSV, TSV, and tabular JSON
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.dkvpa=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.dkvpc: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.csvcolor,shape,flag,k,index,quantity,rate yellow,triangle,true,1,11,43.6498,9.8870 red,square,true,2,15,79.2778,0.0130mlr --csv --ofs pipe head -n 2 docs/src/example.csvcolor|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.dkvpa:1;b:2;c:3 a:4;b:5;c:6mlr --fs ';' --ps : cut -o -f c,a,b docs/src/data/modsep.dkvpc: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.dkvpc:=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.txtoh say can you see by the dawn's early light what somlr --ifs ' ' --repifs --inidx --oxtab cat docs/src/data/extra-spaces.txt1 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-aliasesascii_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-aliasesspaces = "( )+" 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 的支持程度差异很大。官方文档给出了完整的对照表:
| RS | FS | PS | |
|---|---|---|---|
| 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 的分隔符体系可以概括为三句话:
- 三层分隔:记录分隔符(RS)切记录、字段分隔符(FS)切字段、键值对分隔符(PS)切键值,三者分别有输入(
I)与输出(O)两个版本,共六个独立可配置项。 - 按需取舍:DKVP、XTAB 等格式三类分隔符齐全且全部可编程;CSV/TSV 无键值对分隔符但字段分隔符可编程;JSON/YAML/DCF/recutils 等格式的分隔符由规范决定,不可修改。
- 用对工具:普通场景直接用
--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
相关推荐
antd Space 分隔符(separator)完全指南:相邻组件间隔符的配置方式与渲染原理
antd Space 分隔符(separator)完全指南:相邻组件间隔符的配置方式与渲染原理 Ant Design 的 Space https://link.
前端UI组件设计系统StarRocks split 函数完全指南:字符串按分隔符切分与 ARRAY 化处理
StarRocks split 函数完全指南:字符串按分隔符切分与 ARRAY 化处理 split 是 StarRocks 提供的字符串处理函数,用于将一段字符
数据库OLAP数据仓库大数据湖仓一体数据分析SeaTunnel Split Transform 详解:按分隔符拆分字段的配置、行为与源码原理
SeaTunnel Split Transform 详解:按分隔符拆分字段的配置、行为与源码原理 Split Transform 是 SeaTunnel( se
数据集成ETL大数据批处理流处理变更数据捕获
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考