我遇到过不少这样的场景:从网上下载了一个几个 GB 的压缩包,解压的时候发现是一堆 .zip、.z01、.z02 结尾的文件,在 Windows 上双击第一个 .zip 可能就直接解了,可换到 Linux 服务器上,unzip 直接甩脸子给你看,要么报 "cannot find zipfile directory",要么说 "bad zipfile offset (local header sig)"。很多运维和开发同学的第一反应是"这包是不是坏了",其实不是,这不过是分卷 ZIP(split ZIP archive)而已,Linux 默认的 unzip 并不直接支持这种格式。
这篇东西就是围绕"Linux 解压分卷 ZIP 压缩包"这个事写的,适合刚摸 Linux 的新手,也适合那些要在服务器上处理别人丢过来的分卷压缩包的人。我会把背后的原理讲清楚,再把几种能用的方法一个个列出来,包括合并、直接用 7z 解、写脚本批量处理,最后把我踩过的坑和排查思路一并交代。保证你看完能直接照着操作,而不是只会用搜索框碰运气。
1. 为什么在 Linux 上解压分卷 ZIP 这么容易踩坑
1.1 分卷 ZIP 的来源跟 Windows 习惯脱不了干系
分卷 ZIP 最常见的使用场景就是跨平台传输大文件。早年网盘、邮件附件都有单文件大小限制,超过 2GB 或者 4GB 的文件没法直接传,有人就用 WinRAR、2345 好压、7-Zip 这类 Windows 工具把大文件压成多个小分段,每个分段几百 MB,方便上传下载。结果传到 Linux 服务器上,就出现了 .z01、.z02 这种后缀。
这里有个关键认知:分卷 ZIP 不是把一个大 ZIP 文件用普通方式切成几块,而是压缩软件在创建压缩包时,按预设的分卷大小生成的多文件集合。其中第一个分卷通常以 .zip 结尾,后续分卷以 .z01、.z02……依次编号。如果你用 ls -lh 看,这些文件的大小基本一致,最后一个分卷可能小一些,因为它是收尾的。
1.2 Linux 自带工具的历史包袱
Linux 自带的 unzip 工具来自 Info-ZIP 项目,这个项目支持读取 ZIP、ZIP64,但对分卷 ZIP 的支持非常有限。早期版本甚至完全不认识 .z01 文件,你直接 unzip xxx.zip,它只会尝试读第一个 .zip 文件里的中央目录,而这个中央目录恰恰因为分卷机制被拆到其他分卷里去了(分卷较小时尤其明显),于是报错。
所以不是你不会用,是工具本身就不想兼容这事。Info-ZIP 官方其实给出了一个迂回方案:用 zip -s 0 把分卷合并成单个 ZIP,再用 unzip 解压。这个方案可靠,但多了一步操作。很多人不知道,就会卡在第一个报错上。
1.3 分卷 ZIP 跟普通 ZIP 的内部结构差异
要理解为什么 unzip 会报错,得稍微看下 ZIP 的文件结构。普通 ZIP 文件末尾有一个 End of Central Directory Record(EOCD),里面记录了整个压缩包的目录偏移量。unzip 打开文件时,会先跳到末尾找 EOCD,然后根据偏移量去读中央目录。
而分卷 ZIP 的 EOCD 并不一定在第一个分卷里,可能在最后一个分卷里,也可能拆分了。当你只给 unzip 传第一个 .zip 时,它读到末尾发现找不到 EOCD,自然就报 "cannot find zipfile directory" 这类错误。说白了,它根本不知道该去哪里读取完整的文件索引。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 解压前先看清家底:分卷文件的命名与完整性检查
2.1 核对文件列表,别漏了分卷
拿到一堆分卷,第一件事不是急着去搜命令,而是先看全不全。用 ls -l 看看是不是按顺序排列:
bash复制$ ls -lh /data/bigdata/
-rw-r--r-- 1 root root 500M Jun 12 10:23 backup.zip
-rw-r--r-- 1 root root 500M Jun 12 10:23 backup.z01
-rw-r--r-- 1 root root 500M Jun 12 10:23 backup.z02
-rw-r--r-- 1 root root 180M Jun 12 10:24 backup.z03
正常情况应该是这样:.zip 在最前,后面跟着 .z01、.z02、.z03,最后一个分卷大小通常会偏小。如果缺了中间某一个,后面解压基本都会失败,可以先去找对方补传文件。
有个细节容易忽略:有些工具生成分卷时并不用 .z01 这种后缀,而是用 .001、.002 这种纯数字结尾。比如使用某些云存储的分片工具。这种其实是"分片文件"而非"分卷ZIP",处理方式不一样。判断标准很简单:用 file 命令看一眼文件类型:
bash复制$ file backup.zip backup.z01 backup.z02
backup.zip: Zip archive data, at least v2.0 to extract
backup.z01: Zip archive data (split)
backup.z02: Zip archive data (split)
只要出现 Zip archive data,就说明是真正的 ZIP 分卷格式。如果显示的是 data 或者 ASCII text,那就不是标准分卷,合并思路要另想。
2.2 用校验和确认每个分卷没损坏
分卷文件在网络传输中容易损坏,尤其是用 FTP、网盘下载的场景。下载完先算一下校验和,能在解压前就排除一半的坑:
bash复制$ md5sum backup.zip backup.z01 backup.z02 backup.z03
但这里有个问题:你手上可能没有来源方给的官方校验值。没有的话,可以把所有分卷合并成一个文件后再算 md5sum,跟对方的单文件校验值比对。这个放到后面合并步骤一块说。
如果你知道分卷是从一个完整 ZIP 切出来的,还有一个办法:用 zip -T 测试合并后的文件是否完整。不过 zip -T 只能测完整 ZIP,分卷状态下它同样不认。所以更稳妥的做法是:先合并,再测试,再解压。
2.3 注意文件名里的空格和中文
Linux 终端里处理文件名最好加引号,或者用 Tab 补全。分卷文件如果是从网盘下载的,经常带着 (1) 这种字符或者中文名,比如 我的备份 (1).zip。直接用裸文件名很容易出幺蛾子。建议先统一处理一下:
bash复制$ cd /data/bigdata
$ for f in *; do mv "$f" "$(echo "$f" | tr ' ' '_')"; done
把空格替换成下划线,后面写脚本或敲命令都省心。这个操作不影响压缩包内容,只改文件名。
3. 官方 unzip 不行?其实是你命令没写对
3.1 试试直接解压的报错长什么样
先把最常见的错误现场模拟一下:
bash复制$ unzip backup.zip
Archive: backup.zip
warning [backup.zip]: zipfile claims to be last disk of a multi-part archive; attempting to process anyway, hoping all parts are present
inflating: file1.txt
error: invalid compressed data to inflate
或者:
bash复制$ unzip backup.zip
Archive: backup.zip
End-of-central-directory signature not found. Either this file is not
a zipfile, or it constitutes one disk of a multi-part archive. If the
latter, the file may be corrupted or contain extraneous bytes...
这两种情况都说明 unzip 已经把文件当成多卷归档处理了,但它自己搞不定剩下的分卷。这不是命令写错,是 unzip 工具本身对多卷 ZIP 的支持就是不完整的。Info-ZIP 的 unzip 官方文档里明确写了:它只能读取多卷 ZIP 的第一卷,且要求中央目录也在第一卷里。而现代压缩工具产生的分卷,中央目录往往在最后一卷,于是 unzip 毫无办法。
3.2 用 zip -s 0 把分卷合并成单文件
Info-ZIP 配套的 zip 命令里有个隐藏功能:-s 参数用于处理分卷,-s 0 表示把分卷合并成单个 ZIP 文件。命令格式:
bash复制$ zip -s 0 backup.zip --out backup-single.zip
注意 --out 指定合并后的输出文件名。执行过程大概长这样:
bash复制$ zip -s 0 backup.zip --out backup-single.zip
Copying zip entries from backup.zip
Copying zip entries from backup.z01
Copying zip entries from backup.z02
Copying zip entries from backup.z03
跑完以后会生成一个 backup-single.zip,这个文件就是普通 ZIP,不依赖任何分卷。然后就可以正常解压:
bash复制$ unzip backup-single.zip
也可以先测试一下再解压:
bash复制$ unzip -t backup-single.zip
这一步能非常明确地告诉你合并出来的文件是不是完整的。如果 unzip -t 输出 "No errors detected in compressed data of backup-single.zip",那基本就稳了。
3.3 合并时最常见的失败:/tmp 空间不足
zip -s 0 的合并过程不是流式处理,它要把所有临时数据写到临时目录里。默认临时目录是 /tmp,如果分卷总大小有 10GB,而 /tmp 只有 2GB,它就会在合并途中报 "No space left on device"。
解决办法是在执行时指定临时目录,放到磁盘空间足够的路径下:
bash复制$ export TMPDIR=/data/tmp
$ zip -s 0 backup.zip --out backup-single.zip
或者用 -T 参数指定临时目录?等等,zip 没有 -T 这种用法。正确做法是设置环境变量 TMPDIR,或者用 --temp-path 选项?我查过 Info-ZIP 的 zip 3.0 文档,它支持 --temp-path 吗?实际上 Info-ZIP zip 没有这个长选项,它通常通过 TMPDIR 或 TMP 环境变量决定临时文件路径。所以上面 export TMPDIR 的做法是正确的。拿不准的时候,可以先 df -h /tmp 看看剩余空间。
3.4 zip -s 0 的适用边界
zip -s 0 适用于标准 ZIP 分卷,而且要求分卷本身没有加密?不对,加密与否不影响合并。如果分卷文件被改过名,导致顺序错乱,zip -s 0 可能会报 "Cannot find zipfile directory"。它默认按文件名顺序读取,所以不要乱改编号。
此外,zip -s 0 只解决"合并"这一步,不能直接解压。它生成的是完整 ZIP 文件,之后再交给 unzip 或 7z 都行。如果你只是临时解压一下,不想额外生成一个大文件占空间,可以跳过合并,直接用 7-Zip。
4. 用 7-Zip(p7zip)一条命令解压分卷,为什么我推荐它
4.1 安装 p7zip
Linux 下用的 7-Zip 命令行版本是 p7zip,或者更新一点的 7zip 包(7-Zip 官方 Linux 版)。不同发行版安装方式:
bash复制# Debian/Ubuntu
sudo apt install p7zip-full
# RHEL/CentOS 7
sudo yum install p7zip p7zip-plugins
# RHEL/CentOS 8+/Fedora
sudo dnf install p7zip p7zip-plugins
# Arch Linux
sudo pacman -S p7zip
装完确认一下版本:
bash复制$ 7z i | head -20
如果系统提醒 7z 不是命令,说明你装的是 p7zip 但包里只有 7za 或者 7zr。7za 和 7zr 功能有阉割,不支持某些格式。建议装 p7zip-full,它提供完整的 7z 命令。这里不用纠结太多,能跑 7z i 的就行。
4.2 7z x 解压分卷的原理
7-Zip 对分卷 ZIP 的支持逻辑很清晰:你只需要指定第一个分卷(也就是 .zip 那个),它会自动去找 .z01、.z02 等后续分卷,然后像解压普通 ZIP 一样输出文件。实际解压命令:
bash复制$ cd /data/bigdata
$ 7z x backup.zip
注意这里的 backup.zip 是第一个分卷。7-Zip 会根据它内部的记录定位到所有后续分卷,所以不需要在命令里把 .z01 列出来。
执行过程输出类似:
code复制7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,4 CPUs)
Scanning the drive for archives:
1 file, 524288000 bytes (500 MiB)
Extracting archive: backup.zip
WARNINGS:
There are data after the end of archive
--
Path = backup.zip
Type = zip
Physical Size = 524288000
Date Time Attr Size Compressed Name
------------------- ----- ------------ ------------ ------------------------
2024-06-12 10:23:20 ..... 1234567 1234567 documents/report.pdf
...
这时候 7-Zip 其实是把所有分卷当成一个虚拟文件来读取的。它不会生成一个合并后的单文件,而是直接流式解压,所以不需要额外的临时空间,这是它比 zip -s 0 更高效的地方。
4.3 分卷缺失时 7-Zip 的行为
如果某个分卷缺失或者顺序不对,7-Zip 会报 "Can not open file as archive" 或者 "Cannot open file backup.zip as archive",也可能提示 "Unexpected end of archive"。遇到这种提示,先别急着怀疑工具,回去检查分卷文件是否齐全。
比如只有 backup.zip 和 backup.z01,少了 backup.z02,但 backup.z03 又在,这时候 7-Zip 大概率是解压到一半卡住,或者直接拒绝。我的建议是:在解压前用脚本检查 .z01 到最后一个分卷是否连续存在,别等解压到一半才报错。
4.4 p7zip 解压大文件的潜在坑
虽然 7-Zip 解压分卷方便,但它默认会把所有文件解压到当前目录,不会自动创建顶层目录?实际上是按照压缩包里的路径信息创建目录的。如果压缩包里的路径特别深,或者文件名很长,可能触发 Linux 文件名的 PATH_MAX 限制,报 "File name too long"。这种问题一般出现在极端情况下,普通用户很难碰到,但如果你处理的是来源不明的压缩包,还是用 -o 指定一个尽量短的输出路径比较稳妥:
bash复制$ 7z x backup.zip -o/data/out
4.5 7z e 和 7z x 的区别
很多人分不清 7z e 和 7z x。简单说:e 是把所有文件解压到同一个目录,不保留压缩包内的目录结构;x 是完整解压,保留目录结构。处理分卷 ZIP 时,用 x 才是正确习惯,因为分卷里通常打包了多层目录,用 e 会让文件全部散落在一个目录里,同名的直接互相覆盖,亏大了。
bash复制$ 7z e backup.zip # 不推荐,文件全堆在根目录
$ 7z x backup.zip # 推荐,保留目录结构
5. 更极客的方式:用脚本自动完成合并与解压
5.1 一个处理"有序分卷"的 Bash 脚本
服务器上经常要处理别人传上来的分卷,每次手动敲命令太累了。我写了一个小脚本,流程是把所有分卷合并成单文件,然后测试、解压,一条龙完成。核心逻辑是先找第一个 .zip 文件,再用 zip -s 0 合并,最后 unzip 解压:
bash复制#!/bin/bash
# split_zip_extract.sh —— 分卷ZIP自动解压脚本
# 用法: ./split_zip_extract.sh <第一个分卷.zip>
set -euo pipefail
FIRST="$1"
BASENAME="${FIRST%.zip}"
OUT="${BASENAME}-merged.zip"
EXTRACT_DIR="${BASENAME}_extracted"
if [ ! -f "$FIRST" ]; then
echo "错误: 找不到 $FIRST"
exit 1
fi
echo "[1/3] 合并分卷 -> $OUT"
zip -s 0 "$FIRST" --out "$OUT"
echo "[2/3] 测试合并文件完整性"
unzip -t "$OUT" > /dev/null && echo "测试通过"
echo "[3/3] 解压到 $EXTRACT_DIR"
mkdir -p "$EXTRACT_DIR"
unzip -q "$OUT" -d "$EXTRACT_DIR"
echo "完成: $EXTRACT_DIR"
这个脚本有几个细节值得说:
set -euo pipefail保证任何一步出错就停,避免中途失败还继续往下跑。unzip -t测试通过才真正解压,防止压出一个坏文件。- 解压到独立目录,不污染当前目录。
5.2 用 7z 的脚本:更省磁盘空间
如果磁盘空间有限,不想合并出一个临时文件,可以改用 7z 直接解压:
bash复制#!/bin/bash
# 7z_split_extract.sh
set -euo pipefail
FIRST="$1"
EXTRACT_DIR="${FIRST%.zip}_extracted"
mkdir -p "$EXTRACT_DIR"
7z x "$FIRST" -o"$EXTRACT_DIR"
这脚本简单到没什么好说的,唯一要注意的是 7z 的输出路径参数 -o 后面不能有空格,否则会被当成要解压的文件名。
5.3 处理文件名乱码的脚本补充
Windows 常见的中文文件名在 Linux 下解压出来可能是乱码,原因是压缩包内编码是 GBK/GB18030,而系统 locale 是 UTF-8。unzip 默认不处理这种转换,需要配合 iconv:
bash复制$ unzip -O GBK merged.zip -d out/
注意 -O 参数是 Info-ZIP unzip 6.0 以后才有的,老版本不一定支持。如果不支持,可以安装 unzip-iconv 或者改用 7z 加 -mcp 参数?7z 没有直接转编码的参数,但它有一个环境变量 UNZIP_DISABLE_ZIP64?不对,那是另一个方向。
实际上对于乱码文件名,更稳妥的办法是解压后用 convmv 批量转码:
bash复制$ convmv -f GBK -t UTF-8 -r --notest out/
或者直接用 Python 脚本处理。不过大多数时候,分卷压缩包都是从英文环境传过来的,文件名本来就是英文或 ASCII,这一节的内容算是进阶补充。
5.4 用脚本时需要避开的安全提醒
脚本里如果处理来自不可信来源的分卷,需要留意压缩包内是否有路径穿越漏洞(比如 ../../evil.sh 这样的文件名)。加上 -d 指定输出目录后,unzip 和 7z 一般会拦截路径穿越,但保险起见,解压后检查一下有没有异常文件:
bash复制$ find EXTRACT_DIR -name "*..*" -o -name "*.sh" | head
这不是分卷 ZIP 特有的问题,但集中处理外部文件时多留个心眼没坏处。
6. 分卷 ZIP 解压失败的高频故障与我的排查心得
6.1 故障一:CRC 校验失败
这是最常见的错误之一:
bash复制$ unzip -t backup-single.zip
bad CRC abcdef12 (should be 89abcdef)
出现这种情况,说明某个分卷在传输过程中损坏了。排查思路:
- 先重新下载报错的那个分卷,比如 CRC 失败发生在解压
bigfile.bin时,通常对应某个数据块,但不一定能直接定位到具体分卷。 - 把每个分卷各自算一下 md5,跟来源方比对。
- 如果没有比对条件,重新下载所有分卷,这是最笨但最有效的办法。
这里提醒一下:千万不要以为 zip -s 0 合并时能修复损坏,它只是拼接,不会做数据修复。损坏了就是坏了,合并出来也是坏的。
6.2 故障二:合并时报 "Cannot find zipfile directory"
如果在执行 zip -s 0 时遇到:
bash复制zip warning: backup.z01 not found or not readable
说明 /data/bigdata 目录下没有 backup.z01,或者文件权限不足。先 ls -l 看一下是否存在,再看文件是不是只读、当前用户有没有读权限。还有种情况是分卷文件命名不连续,例如缺少 backup.z02,合并同样会失败。
有些分卷 ZIP 的第一个文件后缀不是 .zip,而是 .z00 开头?实际上标准分卷是第一个 .zip,后续 .z01。如果遇到 .z00、.z01 这种命名,说明可能是其他压缩工具的分卷方式,先 file 确认再说。
6.3 故障三:能解压但文件名是乱码
这个在上面提过,根源是编码不一致。如果你确认压缩包内容有中文,解压后文件名是 � 这样的替换符,可以用以下两种方式处理:
- 在支持
-O参数的 unzip 上指定编码:unzip -O GB18030 merged.zip -d out/ - 解压后用
convmv批量转码:
bash复制$ sudo apt install convmv
$ convmv -f GBK -t UTF-8 -r --notest out/
注意 convmv 需要先确认原编码是 GBK 还是 GB18030,用错了可能出现二次乱码。可以用 file 输出内容做个交叉判断。
6.4 故障四:磁盘空间不足导致解压中断
分卷 ZIP 出现的原因就是单个文件太大,解压后占的空间往往比压缩包更大。解压前先算一下:
bash复制$ df -h /data
然后看看压缩包内文件的总体积。可以用 7z l backup.zip 直接列出压缩包内文件总大小:
bash复制$ 7z l backup.zip | tail -5
看 Size 列的总计。如果解压目标目录剩余空间不够,提前换目录,别等解压到一半才报 No space left on device。我之前有一次解压一个 50GB 的数据库备份,压缩包才 18GB,结果 /data 只有 30GB,解压到 80% 挂了,白白浪费了一个多小时。
6.5 故障五:分卷数量特别多导致命令行参数过长
如果是几百个分卷,用 7z x backup.zip 没问题,因为只需要指定第一个分卷。但如果某些工具需要你列出多个分卷,可能会碰到 ARG_MAX 限制。这种情况建议改用脚本或者直接把分卷合并后再解压。实际工作里我见过最夸张的是分卷 1000 多个,每个 10MB,这种情况下 zip -s 0 合并时读取文件列表也很快,不会卡在参数上。
6.6 我的一点实操体会
分卷 ZIP 在 Linux 下解压,真没有网上说的那么玄乎。记住一个优先级:能用 7z 就直接用 7z,因为它同时支持自动识别分卷、流式解压、目录结构保留,省掉中间合并步骤;如果服务器上没装 7z,就用 zip -s 0 合并后 unzip。两条路都走不通,再考虑写脚本或者换工具。
我个人实际使用中,更推荐在服务器上同时安装 p7zip-full 和 unzip,因为 zip -s 0 这个合并功能有时还是要用到的——比如你需要把分卷还原成单文件再传给别的系统。还有一个小技巧:解压完成后,别急着删 .z01 分卷,先确认解压出来的文件数量和大小都对得上再清理,否则一旦发现某个文件解压坏了,分卷已删就只能从头再下载了。
最后再分享一个顺手的小操作:如果你经常要处理分卷压缩包,可以在 ~/.bashrc 里加一个别名:
bash复制alias 7zx='7z x -o"${PWD}/extracted" "$1" && echo "解压完成,输出在: ${PWD}/extracted"'
这只是个简单的封装,核心逻辑还是原生命令。真正遇到问题的时候,按我上面说的顺序去排查,基本都能解决。工具这东西,用得多了自然就熟了。
