这个场景解决什么
Windows 上用 GBK(CP936)打包的中文 zip,在 Linux 解压后文件名会变成一堆乱码。根因在 zip 格式本身:文件名以字节形式存放,Windows 用的是本地代码页,而 Linux 默认按 UTF-8 去解读,两边对不上自然就乱了。解决办法有三条路:用支持指定代码页的解压工具、用带补丁的 unzip 指定编码,或者解压后用 convmv 批量改名。
基本信息
| 适用场景 | 解压 Windows 生成的中文压缩包,文件名需要正常显示 |
|---|---|
| 可选工具 | 7z(指定代码页)、unzip(需发行版补丁支持指定编码)、convmv(后处理改名) |
| 根因 | zip 头里的文件名按字节存储,未带 UTF-8 标记时需按来源代码页解码 |
| 建议顺序 | 先 unzip -l 看文件名能否显示,再决定用哪种方案 |
典型使用场景
- 接收同事在 Windows 上打的资料包,中文文件名全部乱码
- 老旧系统导出的数据包,目录层级也都是中文
- 已经解压出一堆乱码名,需要在不重新解压的前提下修好
- 自动化流程里处理来源不确定的压缩包,需要稳定可复现的方案
- 需要核对包内真实文件名,确认与清单是否一致
三种解法
三种解法
unzip -l gbk.zip # 先看清单里的文件名能不能正常显示
unzip -v | head -n 3 # 看本地 unzip 是否支持 -O 指定编码
7z l gbk.zip # 7z 列表
7z x gbk.zip -mcp=936 -o/out/ # 指定 GBK 代码页解压(跨平台最稳)
unzip -O CP936 gbk.zip -d out/ # Debian 系打过补丁的 unzip 可用已解压出乱码名的补救
已解压出乱码名的补救
convmv -f gbk -t utf-8 -r out/ # 默认只预览,不改名
convmv -f gbk -t utf-8 -r --notest out/ # 确认无误后真正执行
ls out/ | head # 核对结果
# 先复制副本再试,改名操作不可逆
cp -a out out.bak && convmv -f gbk -t utf-8 -r --notest out.bak/核对包内真实文件名
核对包内真实文件名
python3 -c "import zipfile; z=zipfile.ZipFile('gbk.zip'); [print(n.encode('cp437').decode('gbk')) for n in z.namelist()[:10]]"
# 该写法适用于未带 UTF-8 标记的包;带标记的包直接用 namelist() 就是正确名字方案对照
| 方案 | 优点 | 局限 |
|---|---|---|
| 7z 指定代码页 | 跨平台一致,不依赖发行版补丁 | 需要安装 7z |
| unzip -O CP936 | 命令最短,习惯用法 | 依赖发行版对 unzip 打过补丁 |
| convmv 后处理 | 适用于已经解错的目录 | 改名不可逆,必须先备份副本 |
| 脚本化核对 | 可批量处理并留存记录 | 需要写少量代码 |
方案选择有一个简单判据:先执行 unzip -v 看本地版本是否支持编码参数。支持就直接用 unzip,不支持就换 7z 指定代码页,这样最省事也最可复现。
convmv 默认只预览、不实际改名,确认无误后还要加参数才执行。对已经解乱的文件做批量改名之前,一定先在副本目录上试跑一遍。