news 2026/9/25 2:05:48

Miller 压缩数据处理实战:GZIP/BZIP2/ZLIB/ZSTD 输入自动检测、`--prepipe` 外部解压与压缩输出完全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Miller 压缩数据处理实战:GZIP/BZIP2/ZLIB/ZSTD 输入自动检测、`--prepipe` 外部解压与压缩输出完全指南
  • 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 官方文档 reference-main-compressed-data.md 为主体,系统讲解 Miller 6 起内建支持的 GZIP、BZIP2、ZLIB、ZSTD 四种压缩格式的读取方式:按文件扩展名自动检测、用--gzin/--bz2in/--zin/--zstdin手动指定、以及通过--prepipe/--prepipex调用任意外部解压程序的通用方案,并覆盖管道、tee语句和-I原地模式下的压缩输出。读完本文,你将掌握在不解压落盘的前提下直接对.gz、.bz2、.z、.zst文件执行mlr数据处理全流程的技能,并理解这些能力在源码层面的实现原理与安全边界。

压缩数据支持总览:Miller 6 的内建解压能力

从 Miller 6 开始,Miller 在进程内部(in-process)直接支持读取以下四种压缩格式,无需外部解压工具:

压缩格式文件扩展名内建解压方式
GZIP.gzGo 标准库compress/gzip
BZIP2.bz2Go 标准库compress/bzip2
ZLIB.zGo 标准库compress/zlib
ZSTD.zst第三方库github.com/klauspost/compress/zstd

这四种格式的读取统一由 pkg/lib/file_readers.go 中的TFileInputEncoding枚举(FileInputEncodingDefault、FileInputEncodingBzip2、FileInputEncodingGzip、FileInputEncodingZlib、FileInputEncodingZstd)标识,并被所有输入格式的记录读取器(CSV、JSON、XTAB、DKVP、NIDX、PPRINT、DCF 等)共享,因此无论你使用哪种文件格式,压缩处理机制都是同一套。

此外,Miller 6 之前就已存在更通用的--prepipe选项,用于调用$PATH中任意的外部解压程序。两条路径并存:内建解压器(进程内)与外部解压器(进程外)。这两种方式的选择逻辑在 pkg/lib/file_readers.go 的OpenFileForRead中清晰可见:prepipe非空时优先走外部管道(openPrepipedHandleForRead),否则再按内建编码打开。

输入自动检测:按文件扩展名识别压缩格式

如果你的输入文件以.gz、.bz2、.z或.zst结尾,Miller 会自动按扩展名判断压缩格式并即时解压。文档中的完整示例(数据文件为仓库内 docs/src/gz-example.csv.gz):

file gz-example.csv.gz
gz-example.csv.gz: gzip compressed data, was "gz-example.csv", last modified: Mon Aug 23 02:04:34 2021, from Unix, original size modulo 2^32 429
mlr --csv sort -f color gz-example.csv.gz
color,shape,flag,k,index,quantity,rate purple,triangle,false,5,51,81.2290,8.5910 purple,triangle,false,7,65,80.1405,5.8240 purple,square,false,10,91,72.3735,8.2430 red,square,true,2,15,79.2778,0.0130 red,circle,true,3,16,13.8103,2.9010 red,square,false,4,48,77.5542,7.4670 red,square,false,6,64,77.1991,9.5310 yellow,triangle,true,1,11,43.6498,9.8870 yellow,circle,true,8,73,63.9785,4.2370 yellow,circle,true,9,87,63.5058,8.3350

注意,mlr --csv sort -f color gz-example.csv.gz与mlr --csv sort -f color gz-example.csv的输出完全一致——解压发生在内存/流中,磁盘上的压缩文件保持原样、不会被修改。这样做的价值在于:压缩数据在磁盘上节省空间,代价是运行时需要额外 CPU 开销完成解压。对于 GB 级的大文件,这是一种典型的"以 CPU 换磁盘"的取舍。

自动检测的实现位于 pkg/lib/file_readers.go 的FindInputEncoding与 pkg/lib/file_readers.go 的openEncodedHandleForRead:

  • 若用户没有通过标志显式指定编码,则依次检查后缀:.bz2→ bzip2、.gz→ gzip、.z→ zlib、.zst→ zstd;
  • 匹配到后缀后,openEncodedHandleForRead将原始文件句柄包上一层对应的解压 Reader。

一个值得注意的实现细节:Go 标准库的bzip2.NewReader与klauspost/compress的zstd.NewReader都只返回io.Reader而不实现io.ReadCloser,因此 Miller 在 pkg/lib/file_readers.go 中分别定义了BZip2ReadCloser与ZstdReadCloser包装类型,把底层文件句柄的关闭职责一并代理,保证解压流可以正常释放资源。

手动指定压缩格式:--gzin/--bz2in/--zin/--zstdin

当输入文件的扩展名不在.gz、.bz2、.z、.zst之列(例如被重命名为.bin、.dat或从流式 stdin 读取),Miller 无法自动判断压缩格式。此时可用以下四个标志显式告知:

mlr --csv --gzin sort -f color myfile.bin # myfile.bin 的内容是 gzip 压缩的

四个标志与格式的对应关系:

标志含义对应自动检测后缀
--gzin按 gzip 解压.gz
--bz2in按 bzip2 解压.bz2
--zin按 zlib 解压.z
--zstdin按 zstd 解压.zst

这些标志在 pkg/cli/option_parse.go 的CompressedDataFlagSection中定义,每个标志的作用都是把options.ReaderOptions.FileInputEncoding设置为对应的枚举值。--gzin的帮助文本明确写着"Done by default if file ends in.gz",即显式标志与扩展名自动检测的效果等价。

与 stdin 配合使用

压缩数据同样可以通过标准输入管道进入 Miller。由于管道没有文件名,扩展名自动检测无从谈起,此时--gzin等标志几乎是唯一的内建解压途径。仓库测试用例 test/cases/io-compressed-input/0008、0009、0010、0015 分别验证了:

mlr --bz2in count -g a < test/input/medium.bz2 mlr --gzin count -g a < test/input/medium.gz mlr --zin count -g a < test/input/medium.z mlr --zstdin count -g a < test/input/medium.zst

对应地,0005、0006、0007、0014 验证了同样的输入文件不带标志、仅靠扩展名自动检测即可读取。这组两两对照的用例(文件方式 vs stdin 方式、自动检测 vs 手动标志)完整覆盖了内建解压的四种格式、两条路径,是理解本主题的最佳测试标本。

外部解压器:--prepipe与--prepipex

基本用法:--prepipe

--prepipe接受$PATH中任意可执行程序的名称,Miller 会对每个输入文件运行一次该程序,把该程序的标准输出接到 Miller 的标准输入上。效果等价于传统的 shell 管道,例如单文件场景下你完全可以自己写:

gunzip < gz-example.csv.gz | mlr --csv sort -f color
color,shape,flag,k,index,quantity,rate purple,triangle,false,5,51,81.2290,8.5910 purple,triangle,false,7,65,80.1405,5.8240 purple,square,false,10,91,72.3735,8.2430 red,square,true,2,15,79.2778,0.0130 red,circle,true,3,16,13.8103,2.9010 red,square,false,4,48,77.5542,7.4670 red,square,false,6,64,77.1991,9.5310 yellow,triangle,true,1,11,43.6498,9.8870 yellow,circle,true,8,73,63.9785,4.2370 yellow,circle,true,9,87,63.5058,8.3350

而--prepipe的收益在于:Miller 会按文件边界,对每个输入文件分别启动一次指定程序,多文件处理时天然保留文件边界,这是手工管道难以优雅实现的:

mlr --prepipe gunzip --csv sort -f color a.csv.gz b.csv.gz

--prepipe的命令可以是任何"从标准输入读取、向标准输出产出 Miller 可接受数据"的程序,因此名义上你可以按需选用系统里安装的各种解压工具,且支持逐文件差异配置:

mlr --prepipe 'zcat -cf' --csv cat data1.zst mlr --prepipe 'xz -cd' --csv cat data2.xz

如果命令自身带参数(flag),必须用引号整体包裹,例如mlr --prepipe 'zcat -cf'。这一点在 pkg/cli/option_parse.go 的实现中也能印证:--prepipe会把紧随其后的整个参数原样存入options.ReaderOptions.Prepipe,参数内的空格不会被重新分词。

该功能非常通用,并不局限于解压工具——你可以用它做任意逐文件过滤器,例如只取每个文件的前 10 行:

mlr --prepipe 'head -n 10' ...

--prepipe与--prepipex的区别

两者都接受解压命令,区别在于运行时如何拼接命令行(见 pkg/lib/file_readers.go 的openPrepipedHandleForRead):

  • --prepipe:按命令 < 文件名方式调用,适用于从标准输入读取的程序,如gunzip、zcat -cf、xz -cd;
  • --prepipex:按命令 文件名方式调用,不插入<重定向,适用于必须以文件名为参数的程序,如unzip -qc。

对应地,pkg/cli/option_parse.go 中--prepipex的解析器会把PrepipeIsRaw置为true,从而在拼接时走prepipe + " " + escapedFilename分支;而--prepipe的PrepipeIsRaw为false,走prepipe + " < " + escapedFilename分支。

优先级:--prepipe会覆盖自动检测

需要特别留意两条优先级规则(文档明确说明,pkg/cli/option_parse.go 的帮助文本也复述了同样的语义):

  1. 只要命令行指定了--prepipe或--prepipex,它就替换掉一切基于文件扩展名的自动检测决定——即使文件叫foo.gz,也会按你指定的外部命令来处理;
  2. 同理,若同时指定了--prepipe/--prepipex,则--gzin/--bz2in/--zin/--zstdin四个内建标志全部被忽略。

原因显而易见:从 pkg/lib/file_readers.go 的OpenFileForRead可以看到,prepipe非空时直接返回外部管道句柄,根本不会进入内建编码的分支。

安全边界:.mlrrc中的限制

由于--prepipe/--prepipex会执行任意外部命令,存在被恶意利用执行意外代码的风险,因此这两个标志不允许出现在.mlrrc配置文件中。实现位于 pkg/climain/mlrcli_mlrrc.go 的handleMlrrcLine:遇到--prepipe、--prepipex(以及同样危险的--load、--mload)时直接返回"未识别",该行被静默忽略。

不过,.mlrrc中可以使用四个固定等价物(白名单式替代,见 pkg/cli/option_parse.go):

可在.mlrrc中使用的标志等价于
--prepipe-gunzip--prepipe gunzip
--prepipe-zcat--prepipe zcat
--prepipe-zstdcat--prepipe zstdcat
--prepipe-bz2--prepipe bz2

这组标志的命令是写死在代码里的常量,不接受用户输入,因此不构成任意代码执行风险。关于.mlrrc的完整用法参见 customization.md。

另一个安全细节:在把文件名拼入 shell 命令前,Miller 会调用 pkg/lib/file_readers.go 的escapeFileNameForPopen做转义——对文件名中的单引号、双引号分别用单引号包裹,对含空白的文件名整体加单引号,从而避免 shell 注入;外部命令本身最终通过 pkg/lib/halfpipe.go 的OpenInboundHalfPipe以/bin/sh -c "..."方式启动(Windows 上为cmd /c),这也是为什么--prepipe天然支持管道符和重定向等 shell 语法。

压缩输出:三种写入压缩文件的方式

以上全部内容围绕压缩输入展开。压缩输出则有三种典型路径:

1. 标准输出管道(最常用)

Miller 默认把结果写到 stdout,因此直接接管道即可:

mlr sort -n quantity foo.csv | gzip > sorted.csv.gz

这是最简单、最通用的做法,任何压缩/加密/传输工具都能以这种方式参与。

2.tee语句内重定向

当使用 DSL 的tee语句 把输出写到文件(而非 stdout)时,可以用tee的重定向语法指定压缩命令。官方示例将example.csv按color拆分成三个 gzip 文件:

mlr --from example.csv --csv put -q ' filename = $color.".csv.gz"; tee | "gzip > ".filename, $* '

生成的文件确认为 gzip 格式:

file red.csv.gz purple.csv.gz yellow.csv.gz
red.csv.gz: gzip compressed data, last modified: Mon Aug 23 02:34:05 2021, from Unix, original size modulo 2^32 185 purple.csv.gz: gzip compressed data, last modified: Mon Aug 23 02:34:05 2021, from Unix, original size modulo 2^32 164 yellow.csv.gz: gzip compressed data, last modified: Mon Aug 23 02:34:05 2021, from Unix, original size modulo 2^32 158

这些压缩输出之后仍可被 Miller 直接读回(自动检测再次生效):

mlr --csv cat yellow.csv.gz
color,shape,flag,k,index,quantity,rate yellow,triangle,true,1,11,43.6498,9.8870 yellow,circle,true,8,73,63.9785,4.2370 yellow,circle,true,9,87,63.5058,8.3350

tee | "gzip > ..."的重定向底层由 pkg/lib/halfpipe.go 的OpenOutboundHalfPipe实现:Miller 把数据写入管道写端,子进程的 stdout/stderr 直接透传,gzip在另一端完成压缩落盘。

3. 原地模式-I:自动重压缩

使用 原地模式 的-I标志时,被覆盖写回的文件在可能的情况下会自动重新压缩:即输入是 gzip 压缩的,输出依然是 gzip 压缩的。这一点由 pkg/lib/file_readers.go 的WrapOutputHandle保证——gzip 与 zlib 都支持写端压缩包装。

但有两个必须知道的限制(详见 reference-main-in-place-processing.md):

  • bzip2 不支持原地重压缩:受 Go 标准库所限(compress/bzip2只读不写),WrapOutputHandle对FileInputEncodingBzip2会直接返回bzip2 is not currently supported for in-place mode错误。换言之,对.bz2文件使用-I会失败;GZIP 和 ZLIB 则可以正常重压缩;
  • gzip 压缩级别不保留:mlr -I ... yourfile.gz会以 gzip 默认级别重新压缩,Miller 不尝试探测或模仿原文件的压缩级别;
  • --prepipe/--prepipex输入不可原地更新:IsUpdateableInPlace(pkg/lib/file_readers.go)明确拒绝带外部管道的输入,因为 Miller 无从知道如何反转该外部命令;同理,http://、https://、file:// 等 URL 输入也不可原地更新。

源码级实现链路小结

把整条压缩数据流串起来看:

  1. 参数解析:pkg/cli/option_parse.go 的CompressedDataFlagSection统一注册全部压缩相关标志,并把选择写入ReaderOptions.Prepipe/PrepipeIsRaw/FileInputEncoding;
  2. 打开输入:pkg/lib/file_readers.go 的OpenFileForRead按"prepipe 优先 → 内建编码 → 扩展名推断"三级决策返回解压后的读取句柄;
  3. 内建解压:openEncodedHandleForRead按编码分别包装 gzip/zlib/bzip2/zstd Reader(pkg/lib/file_readers.go);
  4. 外部解压:openPrepipedHandleForRead拼接命令(区分<与直接传参),OpenInboundHalfPipe以 shell 子进程方式启动(pkg/lib/halfpipe.go);
  5. 压缩输出:stdout 管道、tee | "命令"重定向(OpenOutboundHalfPipe)、-I原地模式的WrapOutputHandle;
  6. 安全护栏:.mlrrc拒绝自由命令(pkg/climain/mlrcli_mlrrc.go)、文件名 shell 转义(escapeFileNameForPopen)。

完整的端到端验证可参考仓库测试目录 test/cases/io-compressed-input,其 16 个用例覆盖了四种格式 ×(自动检测/手动标志)×(文件/标准输入)的全部组合,以及--prepipe与文件/标准输入的配合;测试数据文件位于 test/input/medium.gz、test/input/medium.bz2、test/input/medium.z、test/input/medium.zst。

使用建议与注意事项汇总

  • 优先用内建解压:.gz/.bz2/.z/.zst文件直接传文件名即可,零配置、跨平台一致(纯 Go 实现,无外部依赖);
  • stdin 场景必须手动声明格式:管道没有扩展名,用--gzin/--bz2in/--zin/--zstdin;
  • 奇特的压缩格式或自定义过滤器:用--prepipe(读 stdin 的程序)或--prepipex(读文件名的程序),命令带参记得整体加引号;
  • .mlrrc中只能用白名单四件套:--prepipe-gunzip、--prepipe-zcat、--prepipe-zstdcat、--prepipe-bz2;
  • -I原地模式:gzip/zlib 输入可自动重压缩,bzip2 与 prepipe 输入不行;
  • 文档生成说明:本文所依据的 reference-main-compressed-data.md 由同目录的.md.in源文件经 docs/src/genmd-filter(Ruby 脚本,处理GENMD-RUN-COMMAND等实时执行宏)生成;其中tee压缩示例特意采用非实时呈现,因为 gzip 默认在头部写入时间戳,每次重新生成都会产生文件差异,不利于版本控制——这也是实际使用tee | "gzip > ..."时可能遇到的现象,若希望输出可复现,可自行在命令中加入gzip -n之类的时间戳抑制参数。
  • 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
点击查看免费下载

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

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

LSTM股票基金预测实战:从akshare数据到PyTorch模型回测

简介&#xff1a;这份资源是面向机器学习初学者与高校学生的LSTM股票/基金预测实战项目&#xff0c;适合用作课程设计、期末大作业或入门时序预测的练手案例。压缩包共11个文件&#xff0c;约155KB&#xff0c;包含3个Python脚本负责模型搭建与数据获取、5个xlsx表格存放训练集…

作者头像 李华
网站建设 2026/9/25 2:03:08

C语言怎么练?六大高频题型与刷题路线全拆解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 2:02:29

UART、I2C、SPI、I2S四大串行总线本质区别与工程选型指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华