ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Linux下zip压缩如何排除指定文件?-x参数与备份实战

2026/9/16 18:26:58 拓冰建站 浏览量
Linux下zip压缩如何排除指定文件?-x参数与备份实战 接手一台新服务器给项目做备份我习惯于直接敲zip -r backup.zip /data/app/这条命令。第一次这么干的时候还没什么感觉等压缩包传到本地一解压发现node_modules占了两百多兆日志文件几百个还有个.git目录也堂而皇之躺在里面——备份一个不到 100M 的代码工程硬生生压出了 3G 的垃圾包。这应该也是不少人在 Linux 下用 zip 压缩时的共同痛点压缩命令本身不难难的是怎么把不需要的文件从压缩包里剔除干净。这篇文章就围绕这件事展开把 zip 命令的排除机制、容易踩的坑、以及实际服务器备份场景里的完整思路一次性讲透。不管你是刚接触 Linux 命令的新手还是已经在生产环境里打过很多次包的老手里面提到的几个细节都值得细看。1. 明明有 tar.gz服务器场景里我却常用 zip1.1 zip 在做“跨平台传输”时确实无可替代我知道在 Linux 生态里tar.gz才是绝对的主流压缩率更高、打包速度也不差服务器之间互传文件几乎都是用它。但如果你跟我一样需要频繁把服务器上的文件打包发给 Windows 或 macOS 的同事zip才是那个最省心的选择——Windows 资源管理器原生支持解压 zipmacOS 也一样双击就完事。tar.gz在 Windows 上默认是打不开的你还得让人装 7-Zip 或者 WinRAR一个简单的交付动作立刻变得复杂。如果是做服务器本地备份、或者你有把握接收方一定在 Linux 环境那用tar.gz更合理。但跨平台需求一旦出现zip就是最优解。这也是为什么在很多企业内部明明服务器都是 Linux大家做交付包、上传到对象存储、发邮件附件时依然默认用 zip。还有个更实际的原因zip命令支持追加、更新和测试操作比如zip -u可以只把新增或变化的文件压进去zip -m压缩后自动删除原文件这些在增量备份场景里非常顺手。tar不是不能做增量但实现起来复杂得多日常使用没有必要。1.2 zip 命令的基础操作速查在讲排除文件之前先把最常用的几条命令列出来后面所有的操作都建立在这几个基础命令之上# 将 source_dir 目录递归压缩为 archive.zip zip -r archive.zip source_dir # 查看压缩包里的文件列表不解压 unzip -l archive.zip # 将压缩包解压到指定目录 unzip -d /path/to/extract archive.zip # 只把新文件或更新过的文件加入压缩包 zip -u archive.zip source_dir # 压缩后删除原文件 zip -m archive.zip file1 file2 # 测试压缩包完整性 unzip -t archive.zip这些命令里unzip -l和unzip -t是后续验证排除效果最重要的两个工具。很多人只知道打包不知道验收压缩完直接扔出去结果别人拿到手解压一看才发现一堆垃圾文件混在里面。养成压缩完立刻列一下内容、测一下完整性的习惯能规避掉很大一部分低级问题。2. 排除不需要的文件-x 参数的正确打开方式2.1 一条最简单的排除命令却有一半人写错zip排除文件靠的是-x参数后面跟要排除的路径模式可以写多个。比如zip -r backup.zip /data/app -x */node_modules/*这条命令的意思是把/data/app整个目录压进backup.zip但排除所有层级下的node_modules目录里的全部内容。这里第一个坑就来了-x后面的模式必须用双引号引起来。如果不加引号写成zip -r backup.zip /data/app -x */node_modules/*那么这里的*会被 shell 先展开成当前目录下匹配*/node_modules/*的文件列表。你当前目录下如果没有这样的路径zip 收到的可能是一个空列表排除规则完全失效node_modules依然会被原封不动打进去。更恶心的是你不会收到任何报错压缩包照常生成问题要等解压时才暴露。我见过很多人在网上提问“为什么 -x 排除了还是没有用”点进聊天记录一看十有八九是引号问题。所以这条规则建议直接形成肌肉记忆看到-x顺手打一个双引号再说。2.2 理解 -x 的匹配机制才不会被奇怪的现象坑到zip的-x模式匹配的是“当前工作目录 传入路径”拼出来的完整路径而不是压缩包内部的相对路径。这句话听起来有点绕直接看例子。假设你当前在/opt目录下执行这条命令cd /opt zip -r backup.zip /data/app -x /data/app/node_modules/*因为 zip 打包时看到的是完整的/data/app/...路径所以-x /data/app/node_modules/*能精确匹配到并排除。但很多时候我们更喜欢在目标目录的上一级执行相对路径打包cd /data zip -r backup.zip app -x app/node_modules/*这种写法也能排除。可问题来了很多人会写成-x node_modules/*心想“反正我排除的是 node_modules写这么长干嘛”。结果就是排除规则匹配不上node_modules 依然被打包进去。为什么因为 zip 在匹配路径时是基于它从命令行收到的整个路径这种情况下是app/node_modules/...去匹配的node_modules/*这个模式从头开始就无法命中app/node_modules/...。这也带出了第二个关键建议统一用*/开头的匹配模式。你不需要关心当前工作目录是什么、打包路径是相对还是绝对只要写成zip -r backup.zip /data/app -x */node_modules/*它的匹配逻辑是“只要路径里的任意一级出现了 node_modules 目录就把它下面所有内容排除掉”。这种模式适应性强基本不会因为路径写法不同而翻车。后面我所有的实际命令都推荐用这种形式。2.3 排除目录与排除文件规则写法有区别排除目录和排除文件模式写法不太一样这里容易产生误解。排除目录内的所有内容写法是目录名后面跟/*-x */logs/*如果只写-x */logs在部分 zip 版本里可能会出现目录本身的条目被排除但目录下的文件依然进包的情况。为了行为一致建议总是写*/logs/*。排除所有日志文件写法是-x *.log模式匹配没有“必须从路径开头匹配”的限制*.log能匹配任意目录下以.log结尾的文件。同理要排除临时文件可以写-x *.tmp排除备份文件可以写-x *.bak。多个排除规则可以写在同一个-x后面也可以拆成多个-xzip -r backup.zip app \ -x */node_modules/* \ -x */.git/* \ -x */logs/* \ -x *.log \ -x .DS_Store这种写法可读性最好每一条规则一眼能看懂是干什么的也方便后续增删。3. 实战一条命令备份线上项目目录3.1 先搞清楚目录到底有多大再决定排除什么拿一个典型的 Node.js 项目来演示。假设项目路径是/opt/app/blog我一般先跑两条命令看清楚状况du -sh /opt/app/blog如果输出显示有 2.3G别急着打压缩命令先用du看看到底是哪一层目录占了空间du -h --max-depth2 /opt/app/blog | sort -hr | head -20大概率你会看到这样的结果2.3G /opt/app/blog 1.9G /opt/app/blog/node_modules 250M /opt/app/blog/.git 60M /opt/app/blog/logs看到这个输出基本可以判断这个项目里必须排除的三样东西node_modules依赖随时可以重新安装、.git版本历史不属于交付物、logs日志只会越积越多。3.2 手写排除命令把风险项也一并挡在包外在/opt/app目录下执行cd /opt/app zip -r blog_backup_20240615.zip blog \ -x */node_modules/* \ -x */.git/* \ -x */logs/* \ -x */tmp/* \ -x */.env \ -x *.log \ -x .DS_Store这里除了node_modules、.git、logs还加了几个有实际意义的排除项*/tmp/*临时目录打包意义不大。*/.env环境变量文件通常包含数据库密码、第三方密钥等敏感信息。很多人打包时把.env一起发出去后面出的是安全事件。*.log把所有散落在各处的日志文件都挡在包外不限于 logs 目录下的那些。.DS_Store从 macOS 同步过来的隐藏文件Windows 同事解压后完全用不上。关于是否排除.env不同团队有不同习惯。我个人的做法是默认不打包需要部署时通过专门的配置分发渠道传过去。如果确实有备份环境配置的需求也应该单独加密存放而不是混在代码压缩包里到处发。3.3 排除规则太多时用文件列表管理项目规模一大要排除的目录和文件种类经常超过十条。这时候一条命令写得又长又乱维护起来很不方便。zip 支持从文件里读取排除规则避免命令行过长zip -r blog_backup.zip blog -xexclude.txt注意是-x加文件路径中间没有空格。exclude.txt的内容每行一个模式*/node_modules/* */.git/* */.svn/* */logs/* */tmp/* */cache/* */.next/* */.nuxt/* */dist/* */__pycache__/* */.idea/* */.vscode/* */.env *.log *.tmp *.cache .DS_Store把这份exclude.txt放在项目外比如/opt/backup_config/里备份脚本直接复用新项目要加东西就改一行非常方便。这也解决了另一个问题手写一长串-x规则的时候漏写一条你自己根本察觉不到而用文件管理至少能看到完整的排除清单。3.4 压缩完立刻自查别等交付了再被打脸打包命令执行完不要急着走立刻验证两个点# 看看压缩包有多大 ls -lh blog_backup_20240615.zip # 检查压缩包里是否还残留着不该出现的东西 unzip -l blog_backup_20240615.zip | grep -E node_modules|\.env|\.git/ | head -20如果第二条命令没有输出说明排除规则全部生效了。如果还能看到相关路径那就要回到上一节提到的匹配机制去排查问题。再跑一次完整性校验unzip -t blog_backup_20240615.zipzip 会逐个文件进行 CRC 校验输出末尾显示No errors detected in compressed data才算真正完成。这一步在服务器磁盘空间紧张时尤其重要——如果压缩过程中磁盘被写满zip 会中断并返回非零退出码但很多人不会看退出码直接把残缺的压缩包发出去了。4. 排除失效、解压乱码、假损坏排查链路与处理方案4.1 “排除了为什么还在”——完整排查链路我自己的项目里曾经出现过一次排除失败现象很典型命令里写了-x node_modules/*压缩包里node_modules却原封不动躺在里面。当时按这条链路一步步排查最终定位到问题。第一步确认压缩包里的实际路径长什么样unzip -l backup.zip | grep node_modules | head -5输出显示路径是app/node_modules/...这样的结构。这已经很能说明问题了我的排除模式写的是node_modules/*但实际匹配目标是app/node_modules/helmet/index.js。从路径开头匹配node_modules的话app开头的那一段就让它匹配失败了。第二步检查当前工作目录pwd确认执行 zip 命令时在/opt/app目录下所以打包源路径是app进包路径自然带了app/前缀。第三步把排除模式改成*/node_modules/*重新打包问题解决。第四步顺便检查了压缩包里有没有符号链接混进去。这是另一个隐蔽坑如果项目目录里有指向外部目录的符号链接比如logs - /var/log/myappzip 默认会跟随符号链接把链接指向的真实目录内容也拉进压缩包。哪怕你排除了*/logs/*因为实际进入 zip 的路径可能是app/logs/...这条规则应该能排除掉但如果链接指向的目录很大压缩过程中已经先占用了一段时间的 IO 和磁盘。这种情况建议打包时加-y选项告诉 zip 只保留链接本身不跟随链接指向的内容zip -r backup.zip app -y -x */node_modules/*4.2 中文字符文件名解压乱码不是压缩包坏了是编码问题Windows 上用压缩软件打包的中文文件名 zip拿到 Linux 下用unzip解压经常出来一堆乱码。很多人第一反应是压缩包损坏但unzip -t测出来是好的。原因是 zip 格式本身没有强制规定文件名编码Windows 简体中文环境下很多工具默认用 GBK 存储文件名而 Linux 下的unzip默认按 UTF-8 解码两边对不上就成了乱码。Linux 环境下的处理方案按优先级排序# 方案一unzip 指定编码解压部分发行版支持 -O 选项 unzip -O GBK file.zip # 方案二用 7z 解压对编码兼容性更好 7z x file.zip # 方案三用 bsdtar 解压libarchive 编码识别更智能 bsdtar -xvf file.zip我自己在服务器上更常用方案三因为bsdtar在处理不同来源的 zip 时乱码出现概率明显比unzip低。如果发行版里没有装一下libarchive-tools就有bsdtar了。反过来在 Linux 上用 zip 打包中文文件名文件传给 Windows 用户也可能出现对方解压乱码的问题。目前比较稳妥的做法是确保 Linux 系统 locale 是 UTF-8然后用较新版本的 zip 工具打包文件名会按 UTF-8 写入Windows 10/11 自带的解压功能已经能正确识别 UTF-8 编码的 zip 文件。4.3 压缩包“假损坏”退出码、磁盘空间与测试有一次帮同事排查他反馈 zip 压缩包解压到一半报错原话是“文件损坏了”。我先跑了unzip -t输出明确提示bad CRC某几个文件。这就不是文件名编码问题是文件内容确实有问题。进一步查发现执行压缩命令的服务器当时/tmp分区只剩不到 200M而 zip 打包过程需要临时写入一部分数据磁盘写满后命令直接中断。但同事没有看命令的退出码——如果仔细留意echo $?会输出非 0 值说明压缩并未成功完成。这个案例给了两条教训压缩前确认磁盘余量至少留出目标压缩包预估大小的两倍空间。压缩命令执行后立刻用$?检查退出码并跑unzip -t做完整性验证。操作很简单但能把“发出去一个坏压缩包”这种低级事故彻底挡在门外面。5. 把排除逻辑固化成脚本形成肌肉记忆5.1 一个可直接套用的备份脚本到这一步原理和坑都讲清楚了最后落地一个可以放到生产环境直接用的备份脚本。这个脚本我自己的服务器上就在跑核心逻辑就是“cd 到项目父目录用相对路径打包集中管理排除规则最后校验完整性”。#!/bin/bash # 项目目录备份脚本 # 使用前修改以下配置 BACKUP_DIR/data/backups PROJECT_DIR/opt/app/blog STAMP$(date %Y%m%d_%H%M%S) EXCLUDE_FILE/opt/backup_config/blog_exclude.txt # 确保备份目录存在 mkdir -p $BACKUP_DIR # 进入项目父目录确保压缩包内路径是相对路径 cd $(dirname $PROJECT_DIR) || exit 1 PROJECT_NAME$(basename $PROJECT_DIR) # 执行压缩排除规则从文件读取 zip -r ${BACKUP_DIR}/${PROJECT_NAME}_${STAMP}.zip $PROJECT_NAME \ -x$EXCLUDE_FILE if [ $? -eq 0 ]; then unzip -t ${BACKUP_DIR}/${PROJECT_NAME}_${STAMP}.zip /dev/null 21 \ echo [OK] 压缩完成且校验通过: ${BACKUP_DIR}/${PROJECT_NAME}_${STAMP}.zip \ || echo [WARN] 压缩可能存在问题请手动检查 else echo [ERROR] 压缩失败请检查磁盘空间和权限 exit 1 fi加上定时任务每天凌晨自动备份0 2 * * * /opt/scripts/backup_blog.sh /var/log/backup_blog.log 215.2 常用排除项速查表写exclude.txt的时候下面这份清单可以直接当参考。每一项都是实操中遇到过、确实会混进压缩包的“垃圾对象”。排除对象推荐模式原因Node 依赖目录*/node_modules/*体积巨大随时可重装Git 版本历史*/.git/*仓库历史不属于交付物SVN 元数据*/.svn/*同上日志目录*/logs/*越来越大备份价值低临时目录*/tmp/*通常无保留价值缓存目录*/cache/*可再生成构建产物*/dist/*、*/.next/*、*/.nuxt/*与源码备份需求冲突Python 缓存*/__pycache__/*可自动生成编辑器配置*/.idea/*、*/.vscode/*属于开发者本机配置macOS 元数据.DS_Store基本是噪音环境变量文件*/.env敏感信息涉及安全通用日志文件*.log覆盖未归类目录里的日志表格里每一项都值得你根据自己的项目场景确认一下。比如dist目录如果备份的目的是保留可部署产物可能反而要保留如果备份的是源码交付那就要排除。规则没有绝对的正确答案关键是你知道自己在做什么、为什么这样配。5.3 和 tar 的 --exclude 做个对照防止混淆因为tar在 Linux 里同样高频很多人会在两个命令之间来回切换然后写混参数。对照一下# zip 排除文件 zip -r backup.zip app -x */node_modules/* # tar 排除文件 tar -czf backup.tar.gz --exclude*/node_modules app两者的逻辑完全一样但语法不同。tar用的是--excludezip 用-xtar 的排除模式对目录是否需要带/*要求没那么严格但为了保险同样建议写成*/node_modules这种形式tar 的语义是排除该目录及其下所有内容。两套命令差不多但千万不能把参数给串了——我见过有人在tar命令里敲-x */node_modules/*结果-x被 tar 解释成了“解压”后面的命令行为完全走样。压缩命令看似基础但“排除不需要的文件”这个需求在实际生产里踩坑的概率远比想象中高。从-x的匹配机制到引号问题从符号链接到编码乱码每一项都是真实环境里反复出现过的故障点。我个人在大量打包项目目录后形成的习惯是先看目录结构再动手排除规则固定成模板文件打包完必跑一次unzip -t。这套流程看起来朴素但它确实让我再没有犯过把node_modules发给别人这种尴尬错误。如果你在服务器上做备份也遇到类似困惑照着上面的排查链路走一遍大概率能把问题定位到根上。