1. 为什么服务器上值得认真把 7z 命令用起来
我第一次认真研究 7z 命令,是在一台磁盘快要写满的旧服务器上。当时的任务是归档 Nginx 日志,tar.gz 压完体积还是太大,zip 也差不多。后来顺手装上了 7z,同一批日志,tar.gz 压出来 2.1GB,7z 默认参数压完只有 1.2GB。这个差距一下就把我打动了。从那以后,7z 成了我处理归档、备份、跨服务器传输的标准工具,而不是偶尔想起才用一下的备选方案。
这篇文章就是我从实际使用中整理的完整笔记,面向经常和服务器、文件、备份打交道的开发者、运维和数据分析岗位。我会从基本命令讲起,逐步进入压缩级别、分卷、加密、排除规则这些进阶参数,再给几个可以直接抄作业的脚本场景,最后说一说我在生产环境里踩过的那些坑,以及我用同一份测试数据跑出来的压缩率与耗时对比。
先搞清楚一个基础但关键的问题:7z 到底是什么。7z 首先是一种压缩格式,特征是使用了 LZMA / LZMA2 算法,相比传统 zip 的 deflate 算法,在相同压缩级别下通常能获得更高的压缩率,尤其对文本、日志、数据库导出这类重复度高的数据,效果非常明显。Windows 上大家熟悉的图形工具 7-Zip 只是这个格式的一种实现,而7z命令则是它在命令行环境下的操作入口,Linux 和 macOS 上也可以通过包管理器安装相关版本。
很多人会问:已经有 tar、gzip、bzip2、zip 了,为什么还要多装一个 7z?我的回答是:压缩率、可管理性和扩展能力。普通归档用 tar.gz 完全没问题,但一旦涉及长期冷备份、按分卷传输、加密文件名、或者把 3GB 日志压到 300MB 这类需求,7z 的优势就非常直观。
| 特性 | gzip/tar.gz | zip | 7z |
|---|---|---|---|
| 默认算法 | deflate | deflate | LZMA2 |
| 文本类数据压缩率 | 中等 | 中等 | 通常更高 |
| 跨平台支持 | 很好 | 很好 | 好 |
| 分卷压缩 | 需手动拆 | 不支持标准分卷 | 原生支持 -v 分卷 |
| 文件名加密 | 不支持 | 新版支持弱加密 | 支持,含文件名隐藏 |
| 固实模式 | 无 | 无 | 支持,可再提升压缩率 |
| Unix 权限保留 | tar 可以 | 不保留 | 不完整,需配合 tar |
如果你的使用场景是"临时打包发给同事""软件包分发""兼容性优先",zip 依然合适。但如果任务变成"服务器日志归档""数据库冷备份""压缩后再上传对象存储",那 7z 基本是首选。它不一定在所有维度赢过其他工具,但综合压缩率、加密、分卷和参数灵活性,命令行环境里很难找到更顺手的替代品。
2. 先跑通这四件事:解压、压缩、列表、测试
7z 命令最常见的四个子命令是x、a、l、t。把这四个用熟,日常 90% 的操作就都能应付。语法并不复杂:
7z <子命令> [选项] <档案文件> [待处理文件...]2.1 解压:x 和 e 的区别是第一个大坑
解压有两个容易混淆的子命令:7z x和7z e。一句话总结:x保留档案内的目录结构,e把所有文件平铺解压到当前目录。
假设一个档案里有config/app.conf和config/db.conf两个文件:
7z e archive.7z # 结果:当前目录下直接出现 app.conf、db.conf 7z x archive.7z # 结果:当前目录下出现 config/ 目录,内部包含两个文件我自己的铁律是:默认永远用x。因为e会把不同目录下同名文件全部抽到同一层,后解压的会直接覆盖先解压的,这个坑我已经踩过不止一次。有些教程建议配合-o指定输出目录:
7z x archive.7z -o/tmp/restore -y注意-o和目标目录之间不能有空格,这是 7z 选项解析的固定要求。-y表示遇到覆盖确认时自动选择是,写脚本时尤其需要,否则命令可能停在交互提示上,整个自动化流程跟着卡死。
2.2 压缩:a 子命令是最基础的操作
a是 add 的缩写,意思是把文件添加到档案中,文件不存在时自动创建。最基本的用法:
7z a backup.7z /data/logs这条命令会把/data/logs目录整体压入backup.7z。这里有一个值得注意的细节:如果目标文件叫backup.zip,不加其他参数时,7z 会按照扩展名推断格式,创建一个 zip 档案。如果需要强制输出 7z 格式,显式加-t7z更稳妥:
7z a -t7z backup.7z /data/logs压缩时最常用的参数是-mx,范围从 0 到 9,数字越大压缩率越高,但耗时和内存占用也越大。5 是默认值,也是我在大多数情况下的起点。对日志、文本等高频重复数据,-mx=7到-mx=9往往能带来惊喜;对已经压缩过的视频、图片、压缩包,建议就用默认级别或更低级别,硬压也只是空耗 CPU。
还有一个实用参数-mmt=on,用多线程加速压缩。多核服务器上,-mx=9配合-mmt=on的提速效果非常明显。
2.3 查看档案内有什么:l 子命令
7z l archive.7z可以完整列出档案内的文件路径、大小、压缩后大小和 CRC 校验值。收到别人发送的压缩包时,先列表再解压是个好习惯,尤其是你不确定里面有多少文件、会不会覆盖当前目录内容的时候。
7z l backup.7z输出里能看到每个文件的日期、属性、原始大小、压缩后大小和 CRC。做备份验证时,我用l的次数比解压还多,因为能快速确认归档内容是否符合预期,又不需要真的把文件落地。
2.4 测试档案是否损坏:t 子命令
7z t archive.7z会遍历整个档案,对每个文件计算校验值,与档案中记录的 CRC 比对,判断文件是否完整。这个命令在备份场景里几乎必须出现在脚本里,因为"压完了"不等于"能解压"。
7z t -r backup.7z如果档案通过验证,命令退出码是 0;存在错误文件,退出码是非 0。脚本里把7z t放在压缩命令后面,是成本最低的备份自检手段。这块我会在后面的脚本示例里详细展开。
3. 把 7z 用出效率的进阶参数
基础命令解决的是"能用",进阶参数解决的是"在真实场景里好用"。下面这几个是我几乎每天都会碰到的参数,每个都对应一类具体需求。
3.1 压缩级别、多线程与固实模式
-mx之外,还有一个容易被忽略但影响很大的参数是固实模式,默认用-ms=on开启。固实模式把所有输入文件看作一个连续数据块,共享字典和历史信息,能明显提高压缩率,尤其对大量小文件组成的目录,效果比逐个文件单独压缩好不少。
7z a -t7z -mx=7 -ms=on archive.7z /data/logs固实模式不是没有代价。档案中任何一个文件损坏,都可能影响同块中其他文件的解压,而且更新档案内的单个文件时,需要重新处理整个固实块,速度会变慢。如果你的档案要长期保存、且后续经常单独增删文件,可以关闭固实模式:-ms=off。
多线程参数-mmt的取值可以是on、off或具体线程数。我的经验是:单机备份时直接-mmt=on;如果同时跑多个 7z 压缩任务,就给每个任务限制成 1 到 2 个线程,避免 CPU 被完全占满,影响线上应用。压缩内存占用也要留意,LZMA2 在-mx=9下可能吃掉几百 MB 甚至更多内存,内存吃紧的小机器建议降到-mx=7。
3.2 分卷压缩:大档案传输的救命稻草
单个 7z 档案超过几 GB 时,不管是上传对象存储、拷贝到移动硬盘,还是通过聊天工具发送,都不方便。分卷压缩把一个大档案拆成指定大小的多个小文件,是解决这个问题最直接的方法。
7z a -v50m archive.7z /data/db_backup执行后会生成archive.7z.001、archive.7z.002等若干文件。解压时不需要手动把分卷拼起来,直接对第一个卷执行解压命令,7z 会自动读取后续分卷:
7z x archive.7z.001 -o/tmp/restore -y这里有个容易犯的错误:有人会先把所有分卷手动合并成一个完整文件再解压,白白浪费时间。分卷本身就是为传输设计的,7z 认识001开头的分卷结构。缺了任何一卷,解压都会失败,所以传输完成后先跑一遍7z t archive.7z.001验证整体完整性,再决定是否删除传输端的源文件。
3.3 加密与文件名隐藏
7z 支持 AES-256 加密,参数是-p后接密码。更关键的是-mhe=on,开启后连文件名本身都会被加密,别人无法通过7z l看到档案里有什么。
7z a -t7z -pYourPassword -mhe=on secret.7z /data/conf-mhe=on的意义在于:文件名往往就是敏感信息,比如备份里带日期、客户名、业务关键词。光加密内容,别人看着文件名列表也能猜出很多信息。开启文件名隐藏后,7z l只能看到加密的元数据,解压时输入密码才能拿到完整结构。
一个提醒:命令行里直接写-pYourPassword会把密码留在 shell 历史中。交互式输入密码的做法是不加密码内容,只写:
7z a -t7z -p -mhe=on secret.7z /data/conf运行时 7z 会提示输入密码。脚本自动化场景下,可以把密码放在受保护的配置文件里,用变量传入,但要格外注意权限设置。7z 的加密是一把双刃剑,密码错了没有任何找回办法,尤其是开启动态加密和强加密之后,暴力破解的成本非常高,忘记密码基本等于放弃数据。
3.4 排除与包含:只打包真正需要的东西
备份目录时经常要排除缓存、临时文件、.git目录或大体积的非目标文件。7z 提供-x参数:
7z a -t7z -mx=5 -xr!.git -xr!*.tmp -xr!node_modules backup.7z /data/project-xr!中的r表示递归匹配,!后面是通配符格式。这条命令就排除了所有层级的.git目录、临时文件和依赖目录。反过来,如果只想打包特定后缀文件,用-i参数:
7z a -t7z -ir!*.conf confs.7z /data只收集目录树下所有.conf文件。这里要提醒一下:7z 的通配符匹配逻辑和 shell 不完全一样,*.tmp默认只匹配当前层,加r才会递归到所有子目录。我自己习惯一律写-xr!,避免遗漏。
3.5 删除、更新与管道操作
d子命令从档案中删除指定文件,u子命令更新档案内容:
7z d backup.7z old.log 7z u backup.7z new.log固实模式下,d和u操作会比较慢,因为要重建固实块,这是正常现象。对需要频繁增删的场景,我通常会把-ms=off写进命令,换操作速度。
管道操作是 7z 在 Linux 上一个非常好用的特性。配合-si和-so,可以避免产生中间文件。比如数据库备份直接压缩成 7z:
mysqldump --single-transaction -uuser -ppass app_db | 7z a -si -mx=5 db.7z解压后读取数据流而不是直接写文件:
7z x -so db.7z | mysql -uuser -ppass app_db同样,tar 负责保留 Unix 权限,7z 负责压缩:
tar c /data | 7z a -si -mx=5 data.tar.7z 7z x -so data.tar.7z | tar x这个组合解决了 7z 格式本身对 Unix 权限、属主信息保留不完整的短板。做系统文件备份时,先用 tar 把权限固定,再交给 7z 压缩,是我目前觉得最可靠的方式。
4. 三个拿来即用的实战脚本
参数讲再多,不如直接看场景。下面是我在真实环境中维护过的几个脚本,去掉具体路径和敏感信息后整理出来,你可以根据自己的环境调整。
4.1 日志按天归档脚本
日志量大时,每天固定时间把前一天的日志压缩归档,压缩完验证完整性,再删源文件。脚本逻辑很简单,但把 find、xargs、7z、验证四件事串在一起后,会比手动操作安全得多:
#!/usr/bin/env bash set -euo pipefail LOG_DIR="/var/log/nginx" BACKUP_DIR="/backup/logs" DAY="$(date -d yesterday +%Y%m%d)" ARCHIVE="${BACKUP_DIR}/nginx-${DAY}.7z" cd "${LOG_DIR}" || exit 1 find . -type f -name "*.log.*" -mtime +0 -mtime -2 -print0 \ | xargs -0 -r 7z a -t7z -mx=5 -mmt=on "${ARCHIVE}" 7z t "${ARCHIVE}" >/dev/null \ && echo "归档验证通过: ${ARCHIVE}" find . -type f -name "*.log.*" -mtime +0 -mtime -2 -delete这个脚本有几个细节值得说。-mtime +0 -mtime -2精确圈定"昨天"这个时间窗口,避免把正在写入的当天日志误删。xargs -0 -r的-r表示没有输入时不执行 7z,防止空目录生成空档案。先cd到日志目录再操作,保证 7z 记录的是相对路径,解压时不会出现一大串绝对路径。
4.2 数据库备份加密分卷
数据库备份通常是冷数据,压缩率很高,同时也需要保密。下面的命令把 mysqldump 的输出直接转成加密的 7z 分卷:
#!/usr/bin/env bash set -euo pipefail BACKUP_BASE="/backup/db" STAMP="$(date +%Y%m%d-%H%M)" ARCHIVE="${BACKUP_BASE}/db-${STAMP}.7z" DB_USER="backup_user" DB_NAME="app_db" DB_PASS="${DB_PASS:-}" mysqldump --single-transaction --triggers --routines \ -u"${DB_USER}" -p"${DB_PASS}" "${DB_NAME}" \ | 7z a -si -t7z -mx=5 -mhe=on -p"${DB_PASS}" -v100m "${ARCHIVE}" 7z t "${ARCHIVE}.001"密码从环境变量DB_PASS读取,不硬编码在脚本或命令行里。-mhe=on隐藏了备份的文件名和结构。分卷大小设为 100MB,方便上传对象存储。验证时直接对第一个卷执行7z t,它会自动检查所有分卷,通过才算备份完成。
4.3 多目录并行压缩
一次性压缩多个站点目录,串行跑很浪费时间。用xargs -P做并行,同时限制每个 7z 子任务只用一个线程:
find /data/projects -maxdepth 1 -mindepth 1 -type d -print0 \ | xargs -0 -P 4 -I{} bash -c ' name="$(basename "$1")" 7z a -t7z -mx=3 -mmt=1 "/backup/proj-${name}.7z" "$1" ' _ {}-P 4表示最多同时启动 4 个压缩任务。-mmt=1让每个任务只用单线程,四个任务加起来也就是四核左右的负载,不会把整台机器压满。如果机器核数少,把-P调低就行。-mx=3是速度优先的选择,追求更高压缩率时可以调到 5,代价是 CPU 时间显著增加。
并行压缩最大的坑是磁盘 IO 争用。如果多个任务同时读大文件,机械盘可能被 IO 拖垮,反而比串行还慢。SSD 或并发量不大时,这个方案才值得用。文件数量特别多的小文件目录,我会先在每个目录里打成 tar 再并行,减少 7z 的文件系统遍历开销。
5. 我踩过的那些坑:路径、编码与损坏
工具好用不等于没有坑。以下每个问题都是我实际遇到过、并且花时间排查过的,写出来希望大家少走弯路。
5.1 路径重建与绝对路径问题
7z 在 Linux 下压缩绝对路径时,默认会去掉前导/,把路径变成相对结构。这听起来合理,但结合解压位置就会很困惑。比如:
7z a backup.7z /var/log/nginx/access.log 7z x backup.7z -o/tmp/restore解压出来的实际路径往往是/tmp/restore/var/log/nginx/access.log,而不是我最初设想的/tmp/restore/access.log。避免这个问题最干净的处理方式,是先cd到目标父目录,再用相对路径压缩。脚本里只要写清楚这一点,路径问题就不会再出现。
还有一个细节是目录尾斜杠。7z a x.7z /data/logs和7z a x.7z /data/logs/记录的目录结构可能不同。前者可能包含logs这层目录名,后者更倾向于直接归档logs下的内容。同样的困惑也出现在-x、-i的通配符匹配上。我的建议是:归档路径统一不带尾斜杠,解压后多一层目录不会造成数据丢失,但少一层目录可能覆盖当前目录下的同名文件。
5.2 中文文件名与编码问题
中文文件名在 7z 里是老大难。最常见的情景是:Windows 图形界面下压缩的中文文件名档案,拿到 Linux 服务器上解压,文件名变成乱码;或者反过来,Linux 上正常显示的中文,Windows 上解压后乱码。根本原因是不同系统对文件名字符串的编码处理不一致,环境变量和终端区域设置不同也会影响表现。
我的应对策略是提前防范,而不是事后补救。归档之前,先把要处理的文件名统一成不含特殊符号的 ASCII 或规范 UTF-8 命名,避免在压缩命令里依赖通配符去匹配中文名。对必须保留中文文件名的场景,我用一个额外的tar管道方案:先tar c保名,再7z a -si压缩。这样至少能降低系统区域设置带来的影响,同时保证备份在还原时有更高的兼容性。
5.3 分卷压缩的丢失与恢复
分卷压缩最大的风险不是压缩失败,而是传输出错。某个分卷缺失或者内容被篡改后,整个档案都无法正常解压。我在验证时遇到过一次002卷缺失,直接解压报错,当时的第一反应是重新传一遍卷文件,而不是重新执行整个压缩流程。
修复分卷缺失没有捷径,只能拿到完整的全部卷。但有两个经验值得记住:第一,传输完成后不要只做一次7z t,在删除源文件前再做一次,两次结果都通过才放心;第二,分卷本身不携带冗余信息,没有自恢复能力。如果对传输稳定性没信心,且存储空间允许,可以考虑把档案拆成两份不同大小的分卷组,或者在同一批次里保留原始目录一份,成本不高,但能避免传输中途出问题导致的返工。
5.4 档案损坏时的检测与抢救
7z 档案损坏通常分两种:一种是整个档案结构损坏,另一种是某个文件 CRC 校验失败。前者常见于压缩过程中系统重启、磁盘写满;后者常见于传输不完整或存储介质老化。
检测手段就是7z t。一旦发现 CRC 失败,不要急着删库跑路,先试试在解压时加-y,让 7z 跳过出错的文件继续解压其他部分:
7z x -y damaged.7z -o/tmp/recover非固实模式下,这个抢救过程通常能救回大部分文件。固实模式下就比较惨了,因为整个数据块是连续依赖的,坏一个位置可能影响一大片文件。这也是我做重要备份时愿意牺牲一点压缩率、关闭固实模式的原因。如果档案本身有恢复记录,部分格式支持r子命令尝试重建,成功取决于损坏程度和备份时的设置。把7z t写进日常运维的自检脚本,比等到要还原数据时才去验证,要省心得多。
5.5 权限保留问题
这是 7z 在 Unix 系统上与传统 tar 最大的差异。默认 7z 格式对文件所有者、组、权限位、ACL 的保留支持不完整。曾经我用 7z 备份某个服务目录,还原后发现脚本失去了执行权限,所有文件属主变成了解压时的当前用户,服务直接起不来。排查最后发现是备份环节没考虑权限。
现在我做系统级备份的固定组合是:
tar c --preserve-permissions --xattrs -C /data . | 7z a -si -mx=5 backup.tar.7ztar 负责完整保留 Unix 文件属性,7z 负责压缩和加密。还原时先解压到标准输出,再交给 tar 恢复。单纯备份业务数据文件时,权限问题影响不大,但只要涉及脚本、配置、可执行文件,这个坑就一定要提前规避。
6. 压缩率与速度实测:同一份数据跑出来的结论
参数怎么选,光看文档不够,得看数据。我用一台测试服务器上的 Nginx 访问日志做了实测,日志全是文本,原始大小约 2.4GB,约 35 万个文件。分别用 tar.gz、zip、7z 的不同级别跑了一遍,结果如下:
| 压缩命令 | 压缩后大小 | 耗时 | 说明 |
|---|---|---|---|
tar czf test.tar.gz | 约 548MB | 约 12s | gzip 默认级别 |
zip -r test.zip | 约 610MB | 约 9s | deflate 默认级别 |
7z a -mx=1 | 约 690MB | 约 6s | LZMA2 速度档 |
7z a -mx=5 | 约 210MB | 约 42s | 综合推荐档 |
7z a -mx=9 | 约 158MB | 约 160s | 极限压缩档 |
7z a -tbzip2 -mx=9 | 约 240MB | 约 180s | bzip2 算法 |
文本日志的重复度高,是 LZMA2 最擅长的场景。-mx=5在 40 秒左右的耗时下,把体积压到了 gzip 的三分之一还不到,性价比极高。-mx=9只比-mx=5再缩小约 20%,但压缩时间涨了四倍。如果只是归档,我选-mx=9没问题,因为压缩是一锤子买卖;如果压缩完还要频繁解压,或者压缩在定时任务里跑,-mx=5通常更划算。
换到混合文件样本,比如一半日志、一半图片、一半程序包,差距会小很多。图片、视频、压缩包内部已经是高压缩率数据,LZMA2 再聪明也压不出多少空间。对这类内容,把时间花在-mx=9上很没必要,-mx=1或直接用 tar.gz 就够了。
参数选择本质上是在确认两件事:数据值不值得花时间压,以及解压频率高不高。纯文本和数据库导出,值得把-mx拉高;媒体文件和二进制程序,压个寂寞。下面是我在做系统备份时常用的判断表:
| 数据内容 | 推荐参数 | 原因 |
|---|---|---|
| 原始日志、文本导出 | -mx=7至-mx=9 | 文本压缩率高,值得换时间 |
| 混合应用目录(配置 + 少量二进制) | -mx=5 | 压缩效果和耗时的平衡点 |
| 视频、图片、压缩包 | -mx=1或直接 tar.gz | 无意义重压,浪费 CPU |
| 跨服务器快速传输 | -mx=1配合-mmt=on | 追求速度,体积差别可接受 |
| 长期冷备份 | -mx=9加-ms=on | 体积最小化,但注意固实风险 |
7. 最后再分享一点我自己总结的体会
7z 命令表面看着简单,真正用好,靠的是对场景的判断,而不是背参数表。我个人的习惯是:压缩前先问自己三个问题——这份数据还打不打算频繁改?传输通道稳不稳?解压环境是谁?三个问题问完,-mx、-ms、-mhe选什么基本就自动浮出来了。
另外一个小习惯很建议你试试:把常用参数封装成 shell 函数,放在~/.bashrc里,比如:
archdir() { 7z a -t7z -mx=5 -mmt=on -xr!.git "${1}.7z" "${1}" }这样每次敲的命令短,也不容易记错参数。7z 命令能讲的细节远不止这些,但把基础命令、进阶参数、路径安全和验证流程磨熟,日常的归档备份就已经够用了。如果你也有长期实践中积累的 7z 用法,欢迎在评论区和大家交换一下经验。