“运维备忘录”这个系列我一直想写点“手册上查得到、真上手却总记不全”的东西,zip命令稳稳排在前三。理由很实在:几乎每台机器都装了它,跨平台交付压缩包默认用它,Windows 同事双击就能打开,日志归档、发布包制作、临时传文件都绕不开它。可真正让人卡住的往往不是“怎么压缩一个目录”,而是“为什么解压出来中文名全是问号”“为什么排除规则写了不生效”“为什么 5G 的包在对方机器上打不开”。这篇就把zip命令从用法到坑位一次讲透,从参数分类、压缩级别取舍、分卷策略,到乱码、4GB 边界、权限丢失这些实战问题,都给你捋成可以直接抄的形态。刚接触 Linux 的同学能照着用,天天写备份脚本的老手也能在排查章节捡到几条。
1. 先搞清楚:运维为什么总要掏出 zip 命令
1.1 zip 与 tar.gz 的真实分工
很多人一开始会纠结格式选型,其实答案取决于“包最终交给谁”。tar.gz是 Linux 世界的原生组合,保留属主、权限、符号链接、硬链接,配合管道和--exclude规则极其顺手,缺点是在 Windows 上要靠第三方工具打开,非技术同事拿到文件第一反应是“这什么”。zip反过来,单文件独立压缩、中央目录放在尾部,任何系统自带解压器都能读,代价是对 Unix 元数据的保存在某些场景下不如 tar 完整,比如硬链接它根本不记录。
所以我的实际分工很固定:机器到机器的整目录搬迁、需要保权限的系统备份、要给rsync做前置打包的,走tar;发给外部、发给业务方、发给非技术同事、需要对方在手机或 Windows 上随手打开的,走zip。这条边界划清楚之后,后面的参数选择就不会左右摇摆。
还有一类场景是zip独有的优势:需要从一个大归档里单独取一个文件。tar.gz是流式格式,gzip 层不支持随机访问,想拿包里第 3000 个文件,只能从头解到尾。zip每条目独立压缩,中央目录记录了每个条目的偏移,unzip archive.zip path/to/file直接定位解出,几毫秒的事。做日志取证、翻历史归档的时候,这个差异能省掉大量等待。
1.2 结构决定特性:中央目录带来的三个便利
zip的文件布局可以粗略理解成“一堆独立压缩块 + 尾部一张目录表”。每个文件单独用 deflate 压一遍,压缩块之间互不依赖,最后在文件末尾写一张中央目录,记录文件名、压缩方法、CRC32、偏移量、Unix 权限位、时间戳。这张表带来三个直接好处:单文件随机提取、可以从已有归档继续追加条目、损坏时只影响局部条目而不至于整包报废。
代价也很明显:单文件压缩率不如跨文件压缩,因为 deflate 的滑动窗口只能在单个文件内部滑动,不能利用文件之间的重复内容。归档一堆高度相似的日志时,tar.gz的整体压缩率通常比zip好,因为 gzip 是整条流一起压。这个差距在文本日志上可能有 10% 到 20%,在二进制文件上小得多。知道这一点,就不会在看到“同样的目录,tar.gz 比 zip 小了 800M”时怀疑自己参数写错了。
第三个特性是归档可以原地更新。zip往已有包里加文件时,只需要追加新条目并重写尾部中央目录,不必整个重新压缩。这也是为什么“每天往同一个归档里追加当天日志”这种用法在zip上很自然,在tar.gz上就很别扭——tar.gz追加需要先 gunzip 再 tar 拼接再 gzip,代价高得多。不过要注意,删除条目(-d)和同步(-FS)这类操作会重写整个归档,大包上要谨慎。
1.3 先看版本再动手,别拿手册对不上现实
Info-ZIP 的zip最后一个正式版本是 3.0,发布于 2008 年,此后基本停更;但大量老系统上跑的仍是 2.31 甚至更早。这两个版本之间有几个行为差异会直接让你照着新文档操作却报错,最典型的是-UN=UTF8(强制以 UTF-8 存储文件名)只有 3.0 才有,2.31 上直接报“unknown option”。
所以任何排障的第一步,我都是先敲这两条:
zip -v unzip -v输出里会带版本号、编译日期和支持的压缩方法。看DEFLATE、BZIP2是否在列表里,就能确定-Z bzip2能不能用;看有没有Zip64字样,就能判断大文件支持情况。这三十秒的确认,比后面猜半小时有效得多。
顺带提一个多数人不知道的:zip -h2会输出扩展帮助,把-UN=、-fz、-sf这些冷门选项都列出来,zip -h只给简版。手册页在部分精简系统上被裁掉了,-h2反而是最全的现场文档。
2. 参数拆解:三十多个选项按四组记才不会乱
2.1 路径与递归:决定包内目录长什么样
-r是使用频率最高的选项,但很多人对它的语义理解有偏差。zip -r a.zip dir会把dir作为顶层条目写进包里,解压出来是a.zip → dir/...。而zip -r a.zip .会把当前目录下所有东西写进去,条目名带./前缀,解压时这些前缀虽然会被处理掉,但在某些老解压器上会生成莫名其妙的./目录,我一般避免这么写。
真正容易出事的是-j(junk paths,丢弃路径只留文件名)。它的用途是把散落在多个子目录里的文件压进同一个包且不带目录层级,比如收集一堆构建产物。但不同子目录下同名文件会互相覆盖,zip只会给个 warning 就继续,最后包里少了一半文件你还不知道。用-j之前先find统计一下重名数量,这是我踩过一次之后的固定动作。
-D是不写入目录条目,只在有特殊需求时用(比如某些老解压器遇到目录条目会报错)。-y是把符号链接本身存进包而不是跟随链接指向的内容,这个在打包/usr/local/bin这类满是软链的目录时很关键——不加-y,你打出来的是链接目标的实体文件,体积翻几倍且结构全错。
-X排除额外的文件属性字段(比如 uid/gid、高精度时间戳)。跨平台交付时加上它,能减少一部分老解压器不认 extra field 导致的警告,代价是丢了属主信息。
2.2 压缩级别与算法:-0 到 -9 到底差多少
zip的默认级别是-6。级别对结果的影响不是线性的,我按经验给个区间,你对照自己的场景选:
| 级别 | 相对 CPU 耗时 | 相对体积(文本日志) | 适用场景 |
|---|---|---|---|
-0 | 极低 | 100%(不压缩) | 已是压缩格式的文件、快速打包传输 |
-1 | 约 0.5x | 比-6大约 5%~10% | 当日必须出包、CPU 紧张的机器 |
-6 | 1x(基准) | 基准 | 通用默认,绝大多数场景 |
-9 | 约 2x~3x | 比-6小约 1%~3% | 长期归档、一次性打包、体积敏感 |
从这张表能读出一件事:从-1升到-6是划算的,从-6升到-9基本不划算。文本类内容在-6之后收益急剧衰减,多花两三倍 CPU 换 1% 体积,在几十 G 的日志上意味着多跑几十分钟。我的默认策略是日常归档用-6,长期冷备且只压一次的大包才上-9。
对已经是压缩格式的内容(jpg、mp4、zip、gz、deb、jar),直接-0只做存储。硬压这些文件不会变小,只会白烧 CPU。判断方法很简单,看一下扩展名或者跑一次file命令,别在这上面浪费时间。
-Z是切换压缩方法,zip3.0 支持-Z bzip2(需要编译时带 bzip2 支持)和-Z store(等同-0)。bzip2 在文本上的压缩率通常比 deflate 好 5% 到 15%,但压缩和解压都慢 2 到 3 倍,而且生成的包在部分老 Windows 自带解压器上打不开,对方得装 7-Zip。跨平台交付时我不用它,纯 Linux 内部归档可以考虑。zstd和xz都不支持,别试,会直接报错。
2.3 增量更新与同步:-u、-f、-FS、-d 的区别
这四个选项是zip命令里最容易混的一组,我用一句话区分:
-u(update):归档里没有的新文件加进去,已存在但文件系统上更新的替换掉。-f(freshen):只更新归档里已经存在的条目,不添加新文件。-FS(filesync):在-f基础上,把归档里存在但文件系统上已经没有的条目删掉。-d(delete):按模式从归档里删除条目。
-FS是“让归档和目录保持一致”的开关,看起来很美,但它有两个必须知道的副作用。第一,删除条目会重写整个归档,几十 G 的包上跑一次可能就是十几分钟 IO 抖动。第二,如果你的目录是挂载点或者临时被卸载了,-FS会把归档里对应的条目全部清空。我有一次在维护窗口里目录还没挂上就跑了带-FS的脚本,结果把一整份备份清空了,好在是副本。所以带-FS的命令,我一定在前面加一个目录存在性检查:
[ -d /data/app ] || { echo "source missing, abort"; exit 1; } zip -FS -r /backup/app.zip /data/app-d的用法是zip -d archive.zip "pattern",模式匹配的是归档内的条目路径,同样会被 shell 展开,必须加引号。这个命令在大包上也是重写级别的开销,别在高峰期跑。-m(move)更危险,它压缩完会删源文件,一旦中途失败就是数据丢失,我在生产环境从不使用。
2.4 分卷、加密与完整性校验
-s做分卷,单位支持k、m、g,比如-s 100m。生成的文件是big.z01、big.z02……最后一卷是big.zip。注意这个顺序:最后那一卷才是.zip后缀,很多人按名字排序上传,结果顺序全乱,对方拼不起来。上传到对象存储或者做介质拷贝时,务必按.z01、.z02、……、.zip的顺序排列。
合并分卷用zip -s 0 big.zip --out merged.zip,它会把整个分卷集合读进来重新写成单个文件。这一步需要所有分卷在同一目录,缺一卷会直接报错——这也是它比手工cat安全的地方,手工拼接没有校验,缺卷也能拼出一个坏包。
-e是交互式设置密码,-P是命令行直接给密码。-P在生产环境基本不能用,因为密码会出现在ps aux和 shell history 里,同机器上的其他用户一眼就能看到。真要用密码,至少走-e交互,或者从受限权限的文件里读。
更要紧的一点:zip的加密只支持传统的 ZipCrypto,这个算法在今天只能算“防误看”,不能算真正的加密,公开的工具可以比较快地处理掉。如果内容真的敏感,正确做法是用gpg或同类工具先加密,再打包;或者用支持 AES-256 的归档工具。别把 ZipCrypto 当成合规加密手段。
-T是压缩完成后自动做一次完整性测试,读一遍归档校验 CRC。这个选项我几乎无脑加在脚本里,尤其是一次性生成的大包——早发现比事后发现好。手工校验用unzip -t archive.zip,输出里会逐条列testing: ... OK,最后给一句No errors detected。
3. 上手实操:五个能直接抄的场景
3.1 按时间归档日志:find 与 -@ 的组合
日常最刚需的是“把七天前的日志打成包,然后删掉原文件”。这里zip有个不太常用的能力:-@从标准输入按行读取文件名列表。配合find就能精确控制范围:
cd /var/log/app find . -type f -name "*.log" -mtime +7 -print | zip -@ /backup/logs-$(date +%F).zip这里有三个细节必须注意。第一,-@是按行读的,文件名里含换行就会断,虽然日志文件基本不会,但通用脚本里要心里有数。第二,必须先cd到目标目录再find .,否则find输出的是绝对路径,打出来的包里条目会带完整的/var/log/app/...层级,解压时容易覆盖系统目录。第三,这种用法不会递归展开目录内容,只处理find列出来的具体文件,这恰好是我们想要的精确控制。
配套的清理动作我一般拆成两步而不是合在一起:先打包,用unzip -t校验通过,再执行删除。合成一条命令看似优雅,但只要打包环节出一点岔子(磁盘满、权限不足),删除照样执行,那就是事故。稳妥的写法是把校验结果作为删除的前置条件:
zip -T /backup/logs-$(date +%F).zip || exit 1 find . -type f -name "*.log" -mtime +7 -delete3.2 排除规则:-x 的写法与三个常见坑
-x是排除,但它有几个反直觉的地方。第一条:模式必须加引号。zip -r a.zip dir -x *.log里的*.log会被 shell 先展开成当前目录的 log 文件列表,结果排除规则变成了几个具体文件名,你想要的通配全没了。正确写法是-x "*.log"。
第二条:模式的匹配基准是zip 命令执行时的工作目录,不是-r指定的那个目录。所以zip -r a.zip /data/app -x "*/cache/*"和zip -r a.zip app -x "*/cache/*"匹配到的路径形态不同。我习惯先cd到父目录,用相对路径操作,规则写起来最直观:
cd /data zip -r /backup/app.zip app \ -x "app/cache/*" \ -x "app/logs/*" \ -x "*.tmp" \ -x "*/.git/*"第三条:-x可以写多次,是“或”的关系,但排除目录时必须用目录/*而不是目录。写-x "app/cache"只会排除名字恰好是cache的那一个条目(也就是目录条目本身),里面的文件照样进去。这个坑我见过至少三次,每次都是对方说“我排除了啊”,一看规则少了个/*。
反过来,-i是只包含,规则写法和-x一致,适合“只打某几类文件”的场景。两者不要混用,容易互相干扰,逻辑会变得难推理。
3.3 大目录分卷与合并:容量怎么算
分卷的典型场景是把包塞进有限制的地方,比如单文件上传上限 2G,或者要刻到介质上。这里需要一点估算。假设源目录du -sh显示 40G,历史压缩比大约 4:1(文本日志),压缩后约 10G。如果目标是单卷不超过 2G,那就按 2G 切,得到 5 卷左右。
但实际分卷大小要留余量,因为压缩比不是均匀分布的——前面几个 G 可能是高度重复的日志(压得很狠),后面几个 G 可能是图片和归档文件(几乎不压)。所以我会把单卷定在限制值的 85% 左右,比如限制 2G 就切1700m:
zip -r -6 -s 1700m -T /backup/bigdata.zip /data/bigdata注意-s的单位写法:k、m、g都支持,纯数字默认按字节算,别只写-s 1700结果切出上千个小卷。
分卷生成后如果源目录有变动,不要试图往分卷包里继续追加,行为不可预期。正确做法是重新完整生成。这也是我倾向“分卷包只做一次生成、之后只读”的原因。
传输过程中的校验同样重要。分卷集合在拷贝时容易漏卷或顺序错乱,接收端一定要先验证:
zip -s 0 /backup/bigdata.zip --out /tmp/merged.zip unzip -t /tmp/merged.zip第一步能不能成功本身就是一次完整性检查——缺卷、卷名不对、顺序乱,都会在这一步报错。
3.4 发布包制作:固定结构与权限的五个动作
给业务方或者部署脚本用的发布包,对结构的要求比一般归档严格。我固定用这套动作:
cd /build/workspace zip -r -y -X -D release-1.0.zip app \ -x "app/logs/*" -x "app/tmp/*" -x "*.pid" -x "*.swp" unzip -l release-1.0.zip | head -30 unzip -t release-1.0.zip逐个说理由。-y保留符号链接本身,很多应用目录里current -> releases/xxx这种软链,不加-y会把目标整个打进去,体积暴涨且解压后结构变了。-X去掉额外的属主字段,减少跨机器解压时的干扰。-D不写目录条目,部署脚本通常自己mkdir -p,多余的目录条目反而干扰。
打完包后用unzip -l抽查前几十行,确认顶层结构是app/而不是./app/或者一堆散文件。这一步能挡掉大部分“结构不对”的低级问题。
还有一个容易忽略的点:解压时的属主和权限。zip会记录 Unix 权限位,unzip在 Unix 上解压时会尽量还原,但如果解压者是普通用户,属主没法还原,权限也可能被 umask 削。所以部署脚本里不要在解压这一步依赖权限,把chmod、chown明确写在下一步,比指望归档还原靠谱。
3.5 加密与传输:能做什么、不能做什么
前面说过 ZipCrypto 强度有限,但“防止压缩包被随手打开看到内容”这个需求是真实存在的,比如临时传一份带账号配置的目录。这时候-e交互式设置密码是可用方案:
zip -r -e config-backup.zip config/流程会提示输入两次密码。生成的包在 Windows 自带解压器和unzip上都需要密码,兼容性没问题。要提醒的是,密码别写在脚本里,更别写在 crontab 里。如果确实要做自动化,把密码写进一个 600 权限的文件,用变量读取,并且接受“这台机器上有明文密码”这个事实,用机器权限来兜底。
真敏感的数据,我的做法是分两层:先用gpg对内容加密,再zip打包。这样即使包被拿到,外层只是个普通压缩包,里面的东西是密文。虽然多一步,但安全边界清楚得多。
传完之后对方要验证,可以发一条 CRC 值过去:
unzip -v config-backup.zip | tail -3 sha256sum config-backup.zipunzip -v的输出里带每个条目的 CRC32 和总大小,sha256sum给整包指纹。两个值一起发,对方比对一下就知道传的过程中有没有被动过。
4. 排查实录:那些能让人卡半小时的问题
4.1 中文文件名乱码:本质是编码标记的缺失
这是被问得最多的问题:Linux 上用zip打包中文名文件,Windows 上打开全是乱码;或者反过来,Windows 打的包在 Linux 上解压乱码。根因在于 zip 格式早期对文件名编码没有强制规定,Windows 版工具习惯按本地代码页(GBK 系)写,Linux 版按 UTF-8 写,且默认不设置“这个文件名的编码是 UTF-8”的标志位,对方只能猜。
zip3.0 提供了-UN=UTF8强制以 UTF-8 存储并设置标志,配合支持标志的解压器就能正确显示:
zip -r -UN=UTF8 archive.zip docs/反方向(Windows 打的包在 Linux 解压乱码),如果本机的unzip编译时带了字符集转换,可以用-O指定源编码:
unzip -O CP936 windows-made.zip -d ./out注意-O不是所有发行版的unzip都支持,老版本会直接报“invalid option”。不支持的场合,实用的绕法是用 Python 的zipfile读取并手动指定文件名编码,或者干脆让对方用 7-Zip 重新打包并勾选 UTF-8。长期方案是统一约定:所有跨平台交付的包,压缩端都加-UN=UTF8,把不确定性消除在源头。
4.2 “Nothing to do”和“name not matched”到底在说什么
zip error: Nothing to do!这个提示的信息量极低,但它只对应几种情况。一是要压缩的路径不存在或者没有读权限,zip静默跳过之后发现无事可做。二是-x/-i的规则把所有文件都过滤掉了。三是目标归档名和源路径搞混了,比如zip a.zip a.zip这种自引用。
排查顺序我固定为:先ls -l确认源路径存在且有读权限,再把命令里的-x规则全部去掉跑一次——如果这次正常,问题就在规则上。规则问题里最常见的还是前面说的“模式被 shell 展开”和“排除目录漏了/*”。
zip warning: name not matched: xxx则是模式没匹配到任何条目,属于警告不是错误,但往往暗示你的规则写错了路径形态。比如-x "cache/*"在cd /data之后应该写成-x "app/cache/*"。这类警告不要忽略,它经常是“排除没生效”的第一现场。
4.3 4GB 与 65535:两个硬边界
经典 zip 格式有两处上限:单个归档总大小和中央目录偏移不超过 4GB,条目数不超过 65535。Info-ZIP 通过 Zip64 扩展突破这两个限制,在接近边界时自动启用。问题出在“自动”两个字上:你的机器上启用了,对方的解压工具不一定认。
我遇到过的典型现象是:本地unzip -t一切正常,发到某台老服务器或者某个嵌入式设备上,报“invalid zip file”或者只解出前面一部分。所以做大包时有个固定检查动作:
unzip -v big.zip | head -5如果输出里出现 Zip64 相关提示,或者包总量接近 4GB、条目数接近六万多,就要提前问对方用什么工具解压。zip也提供了-fz强制启用 Zip64、-fz-强制禁用,后者在“必须兼容老解压器且内容确实没超限”时可以一用,但超限了会直接报错,别指望它能变魔术。
更稳的规避方式是控制单包规模:日志归档按月切、发布包按模块切,让单个包远离 4GB。这不是妥协,是减少交接环节的沟通成本。
4.4 权限、符号链接、硬链接:zip 记得什么、忘了什么
zip会记录 Unix 权限位(存在外部属性字段里),unzip在 Unix 上解压时会尝试还原。但属主信息默认也会记(-X会去掉),普通用户解压时属主还原不了,只能落到自己名下。这不是 bug,是权限模型决定的。
符号链接默认被跟随,也就是把链接指向的内容实体打进去。要保留链接本身必须-y。这一条在打包node_modules/.bin、bin目录、current这类软链密集的结构时决定成败。
硬链接完全不保留,zip会把每个硬链接当成独立文件各存一份,体积翻倍,解压后关系断掉。需要保留硬链接的场景只能走tar。
ACL、扩展属性、稀疏文件同样不保留。做系统级备份的时候,这几点足以让你放弃zip改用tar。这里没有优劣,只有匹配不匹配。
4.5 速度慢与资源占用的定位思路
zip是单线程的,一个 40G 的目录用-9压,跑几小时很正常。看到 CPU 只跑满一个核就以为出问题了,其实是设计如此。想提速只有三条路:降级别(-6换-1)、换多线程工具(pigz配合tar)、或者把目录拆成几份并行压再合并。
还有一种“慢”不是 CPU 的问题,是 IO。源目录里混了大量小文件时,瓶颈在元数据操作而不是压缩本身,这时候降级别也没用。可以先find | wc -l看一下文件数量,十万级的小文件目录,考虑先tar成一个流再压,能显著减少系统调用次数。
磁盘写满的表现也要认识:zip会在写到一半时报write error (disk full?),此时归档是损坏的,不能因为文件名存在就当成功了。所有脚本里打包之后都应该跟一次unzip -t,这一条比任何优化都重要。
下面这张表是我自己整理的速查版本,遇到问题时从上往下对照:
| 现象 | 最可能的原因 | 处置 |
|---|---|---|
Nothing to do! | 路径不存在、无读权限、规则过滤光了 | 去掉-x试跑,ls -l确认源 |
name not matched | 排除/包含模式的路径形态不对 | 统一cd到父目录用相对路径 |
| 中文名乱码 | 未设置 UTF-8 标志或源编码不符 | 压缩端-UN=UTF8,解压端-O CP936 |
| 排除规则不生效 | 模式被 shell 展开、目录漏写/* | 全部加引号,目录写成dir/* |
| 对方打不开大包 | 触发了 Zip64 | 拆小包,或确认对方解压器版本 |
| 软链变成实体文件 | 未加-y | 打包命令补-y |
| 解压后权限不对 | 属主无法还原或被 umask 削 | 权限在部署脚本里显式设置 |
| 归档变成 0 字节或截断 | 磁盘满、中途被 kill | 打包后必跑unzip -t |
| 压缩极慢 | 单线程 + 高级别 + 海量小文件 | 降级别,或改用多线程方案 |
5. 把 zip 塞进脚本:几条不能松的硬约束
5.1 幂等与失败可见性
定时任务里的zip最容易出的问题是“看起来跑了,其实没成功”。zip在部分错误下返回码并不直观,比如源目录为空时它返回 12(nothing to do),有些错误只打 warning 却返回 0。所以脚本里不能只看返回码,必须加结果校验。
我的固定模板是“打包 + 测试 + 记录”三步,任何一步不过就退出并留痕:
#!/bin/bash set -u SRC=/data/app DST=/backup/app-$(date +%F).zip LOG=/var/log/backup.log [ -d "$SRC" ] || { echo "$(date -Is) src missing" >> "$LOG"; exit 1; } zip -r -6 -q -T "$DST" "$SRC" >> "$LOG" 2>&1 \ || { echo "$(date -Is) zip failed" >> "$LOG"; exit 2; } unzip -t "$DST" > /dev/null 2>&1 \ || { echo "$(date -Is) verify failed" >> "$LOG"; exit 3; } echo "$(date -Is) ok $(stat -c %s "$DST")" >> "$LOG"-q把逐文件输出压掉,避免日志被刷爆,但错误仍然会进日志。-T让zip自己先测一遍,外面再用unzip -t复核一次,双重保险。最后一行记录大小,方便事后判断“某天的包明显偏小”这种异常。
幂等性方面,如果同一天可能重复执行,用固定文件名就会变成往同一个包里追加。要么在文件名里带时间戳,要么执行前判断文件是否存在并跳过,别让它无限追加下去。
5.2 密码与敏感信息的处理边界
自动化场景下的密码处理只有两个可接受的位置:受限权限的文件,或者外部密钥管理服务。写进脚本、写进 crontab、写进命令行参数,都属于把密码公开。命令行参数尤其危险,因为ps对同机器所有用户可见,不需要任何特殊权限。
如果确实要自动加密,用文件加变量:
PASSFILE=/etc/backup.pass # 权限 600,属主为执行用户 [ -r "$PASSFILE" ] || exit 1 zip -r -P "$(cat "$PASSFILE")" -q out.zip data/即便如此也要清楚:这条命令执行期间,密码仍然会短暂出现在进程参数里。真正严格的场景应该放弃zip加密,改成先gpg加密流再落盘。我个人的判断标准是“这份数据泄露会造成什么后果”,会带来实质损失的,一律不用 ZipCrypto。
5.3 保留策略与空间核算
归档脚本上线三个月后把磁盘写满,是极其常见的翻车方式。核算思路很简单:单日包大小 × 保留天数 × 安全系数。比如单日 2G、保留 30 天、系数取 1.5,需要预留 90G。安全系数是给“某天日志暴涨”留的缓冲。
清理不要用rm加通配符硬来,用find按时间更安全:
find /backup -maxdepth 1 -name "app-*.zip" -type f -mtime +30 -delete-maxdepth 1很重要,防止误删子目录里的东西。-type f排除掉同名的目录。这两个限定条件加上去,误删概率降到很低。
还有一条经验:清理之前先确认最新的包是好的。如果连续几天打包都失败了,清理策略会像推土机一样把仅存的历史备份也推掉。所以在清理脚本前面加一句“最新包存在且能通过unzip -t”的前置检查,成本几乎为零,救命效果拔群。
顺带说个观察:日志类归档按月打包比按天打包更划算。同一个月内的日志重复内容多,跨文件压缩虽然zip做不到,但单文件内部的重复模式仍然能被 deflate 利用,实际压缩率往往比按天打高出一截,文件数量也少一个量级,管理成本低很多。按月打包、保留十二个月,是我目前用得最舒服的组合。