ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Linux tar命令详解:归档、压缩与解压实用技巧

Linux tar命令详解:归档、压缩与解压实用技巧 1. tar命令的本质它跟压缩其实是两码事我刚用Linux那会儿一直以为tar就是个“压缩解压命令”后来才发现这个理解偏差会带来不少问题。其实tar全称是Tape Archive最早的设计目标是把一堆文件打包到磁带这种顺序存储设备上所以它最核心的职责是归档——把多个文件或目录合成一个文件方便存储和传输。而大家习惯的“压缩”功能其实是tar调用外部工具gzip、bzip2、xz完成的。1.1 先搞清楚归档不等于压缩归档和压缩是两个不同维度的操作。归档解决的是“把多个东西收拢成一个”压缩解决的是“把一个大东西变小”。你可以只归档不压缩也可以归档后再压缩。tar命令默认只做归档不做压缩。当你看到tar -czvf这样的写法时-z才是让tar调用gzip进行压缩的参数而不是tar本身就自带压缩能力。这个区别在什么时候体现得最明显就是你随手打一个.tar文件发现它体积跟原目录差不多甚至更大一点。这时候不用怀疑自己操作错了因为没有压缩的tar包只是把文件顺序拼在一起文件本身没有变小还会额外加一些头部元信息。真正想压缩就要显式指定压缩算法。1.2 tar能干什么从备份到传输再到打包发布的场景tar命令的应用场景比我最初想的要广得多。日常用得最多的几个方向备份数据把整个项目目录、日志目录、配置目录打包成一份归档文件定期备份到其他磁盘或远程服务器。传输文件把多个配置文件、依赖包、静态资源打成一个包通过scp或者U盘拷走省去逐个传输的麻烦。发布源码包很多开源软件发布的就是.tar.gz格式的源码包用户下载后通过tar解压再编译安装这是Linux生态里最经典的软件分发方式。迁移环境把整个应用目录连同依赖一起打包拷到另一台机器上解压只要系统环境大致一致就能快速恢复服务。增量备份配合--listed-incremental参数可以只打包自上次备份以来新增或修改的文件这对动辄几十GB的数据目录来说非常实用。1.3 关于tar的版本差异先说什么情况会踩坑Linux发行版自带的tar基本都是GNU tar兼容性很好。但如果你管理的机器里有BusyBox环境很多嵌入式设备、容器基础镜像就是BusyBox它的tar功能和GNU tar是有差别的。最典型的差异是参数写法GNU tar可以省略开头的-比如tar czvfBusyBox的tar通常也支持这种写法但在一些精简版里可能只支持tar -czvf这种带横杠的形式。此外--exclude、--listed-incremental这类长参数在BusyBox的tar里往往不支持。所以写脚本的时候尽量用GNU tar的兼容写法涉及关键备份场景先用tar --version确认一下版本。2. 核心参数解析与速记方法tar的参数看着一堆其实有规律可循。我习惯把它分成三组来记操作参数做什么、压缩参数怎么压、辅助参数附加选项。2.1 参数构成规律tar命令的基本格式是tar 操作参数 [压缩参数] [辅助参数] 归档文件名 源文件或目录举个例子tar -czvf backup.tar.gz /home/user/data拆开看就是-c表示创建归档create-z表示用gzip压缩-v表示显示处理过程verbose-f表示后面紧跟的是归档文件名file。这是最经典的组合也是新手最常用的。2.2 常用参数速查表参数含义使用场景-ccreate创建归档打包文件时必用-xextract解压归档解压时必用-tlist列出归档内容不解压查看包内文件-rappend追加文件到归档末尾往已有tar包追加文件-uupdate更新较新的文件仅将比归档内更新的文件加入-ddiff对比归档与文件系统的差异校验归档完整性-zgzip压缩生成.tar.gz后缀的包-jbzip2压缩生成.tar.bz2后缀的包-Jxz压缩生成.tar.xz后缀的包-vverbose显示文件列表观察打包/解压过程-ffile指定归档文件名必须紧跟文件名-C切换目录解压到指定目录或从指定目录打包--exclude排除匹配的文件或目录打包时跳过缓存、日志等--listed-incremental增量备份元数据文件做增量备份2.3 压缩级别怎么选tar本身没有压缩级别参数它把压缩这件事交给了外部工具。比如用gzip时可以加环境变量或者在管道中指定级别# 用tar调用gzip指定压缩级别为9最高压缩比 tar -cvf - /home/user/data | gzip -9 backup.tar.gz压缩级别从-1最快压缩比最低到-9最慢压缩比最高默认是-6。如果CPU资源紧张而磁盘空间充足用-1反而更划算因为压缩时间能少一半以上体积差距通常只有几个百分点。反过来如果归档文件要长期保存在冷存储上值得花时间用-9把体积压到最小。2.4 xz、bzip2、gzip到底选哪个这个问题在社区里问的人最多。我的选择标准很简单日常使用.tar.gz就够了。gzip解压速度快压缩率适中所有环境都认识它基本没有兼容性坑。追求高压缩比.tar.xz。xz压缩出来的文件比gzip小20%到30%但压缩时间会明显变长。我在备份数据库导出文件时喜欢用xz因为那些SQL文件文本重复度高压缩收益很大。老系统兼容.tar.bz2。bzip2在早期Linux发行版里很常见现在用得少了。除非你明确知道目标环境只有bzip2否则建议优先用xz替代。我做过一次实际对比一个2.3GB的Nginx日志目录gzip压缩后约480MBxz压缩后约360MBxz省了25%的空间但耗时从40秒涨到了3分多钟。如果只是临时传文件gzip性价比最高。3. 实操过程从打包到解压的完整用法看再多参数表都不如直接上手敲几条命令。下面整理的是我在实际项目中频繁使用的tar用法每条都标注了适用场景和注意事项。3.1 打包创建tar.gz压缩包基础命令tar -czvf project-backup.tar.gz /home/user/project这条命令的作用是把/home/user/project整个目录打包并压缩生成project-backup.tar.gz。-c创建-z压缩-v显示过程-f指定输出文件名。一个容易忽略的细节如果你在绝对路径下打包解压时会按绝对路径还原文件。比如用tar -czvf backup.tar.gz /home/user/project打包后在另一台机器上解压它会直接把文件解到/home/user/project下而不是解到当前目录。如果你希望解压时去掉一级目录需要在打包时加-C参数切换到目标目录的上级然后用相对路径打包cd /home/user tar -czvf project-backup.tar.gz project这样解压后当前目录下会生成project文件夹而不是一路覆盖到/home/user。这一点在做环境迁移时特别重要我见过不少同事因为路径问题把文件解错位置最后还得手动搬一遍。3.2 解压处理不同后缀的压缩包解压的通用写法tar -xzvf project-backup.tar.gz tar -xjvf project-backup.tar.bz2 tar -xJvf project-backup.tar.xz后缀不同压缩算法参数就不同gzip用-zbzip2用-jxz用-J。如果不想记这么多GNU tar还有一个更省事的办法——直接不写压缩参数tar -xvf project-backup.tar.xz新版GNU tar会自动检测压缩格式无论它是gzip、bzip2还是xz都能正确解压。不过老版本或者BusyBox环境不一定有这个能力所以写自动化脚本时最好还是显式指定压缩参数避免环境差异导致解压失败。解压到指定目录用-Ctar -xzvf project-backup.tar.gz -C /opt/deploy3.3 查看包内内容而不解压这个场景我经常用。比如你下载了一个源码包想先看看里面有什么文件、目录结构是什么样不用解压就能直接看tar -tzvf project-backup.tar.gz-t参数列出归档内容-v参数显示详细信息包括文件权限、属主、大小、修改时间。这样可以在解压前快速确认包内是否有恶意路径比如/etc/passwd这种绝对路径或者溢出目录../../这种相对路径。安全方面这一点对企业内部文件交换尤其重要。如果想从归档中单独提取某个文件不全部解压可以这样tar -xzvf project-backup.tar.gz var/log/nginx/access.log这条命令只解压出归档里的var/log/nginx/access.log这一个文件。运维排障时最常用这个能力不用把整个包摊开就能取日志。3.4 向已有tar包添加或更新文件偶尔会遇到这种情况你打包完了发现漏了一个配置文件或者某个文件有更新。tar支持往包里追加文件tar -rzvf backup.tar.gz /home/user/project/config.yml-r参数把文件追加到归档末尾。注意只有当归档文件是未压缩的.tar格式时才能追加.tar.gz这类压缩归档不支持直接用-r追加。因为压缩是整体流式处理的没法在压缩流末尾简单追加数据。我之前为了给已经压缩的包追加文件只能先解压、再重新打包虽然麻烦但这是最稳妥的方式。对比更新用-utar -uzvf backup.tar /home/user/project/config.yml-u会比较归档内文件的修改时间和源文件的修改时间只有当源文件比归档内更晚修改时才用新文件替换旧条目。这个同样只适用于未压缩的tar包。3.5 排除不需要的文件打包时最常见的需求是什么排除node_modules、__pycache__、.git、日志文件这些“垃圾”目录。手动一个个删再打包太笨了直接用--exclude参数tar -czvf app.tar.gz /home/user/app \ --excludenode_modules \ --exclude__pycache__ \ --exclude*.log这里有三点需要注意--exclude要在源目录之前还是之后实际上tar对参数顺序没有那么敏感但为了清晰我习惯把它放在源目录后面用反斜杠换行方便阅读。通配符模式*.log会排除所有扩展名为.log的文件。如果只想排除某个特定路径下的日志写全相对路径更精确。排除项语法如果是在打包目录的相对路径下排除模式里不要带前导/否则可能匹配不上。还有一种排除技巧是针对多个同名称目录的比如排除所有环境中的node_modulestar -czvf app.tar.gz . \ --exclude*/node_modules \ --exclude*/vendor这个*/前缀表示任意层级目录下匹配到的名称实测下来比单写node_modules更可靠。3.6 增量备份只打包新增或修改的文件维护服务器时完整备份一个大目录非常耗时耗空间。tar提供了增量备份机制核心是--listed-incremental参数加一个快照文件# 做第一次全量备份 tar -czvf backup-full.tar.gz --listed-incrementalbackup.snar /home/user/data # 过几天做增量备份 tar -czvf backup-inc1.tar.gz --listed-incrementalbackup.snar /home/user/data这里的backup.snar是一个状态文件记录备份时哪些文件已经被处理过。第一次运行时全部文件都算“新增”所以是全量备份之后再运行时tar会参考快照文件只处理新增或内容有变化的文件输出增量备份包。恢复增量备份时需要按顺序解压先恢复全量包再解第一个增量包再解第二个增量包……顺序错乱会导致数据状态不对。这套方案适合没有专门备份软件、只依赖系统自带命令的轻量场景。3.7 tar配合管道用一条命令完成打包和远程传输tar的另一个强大之处是它可以输出到标准输出配合管道和ssh实现“打包后直接传到远程服务器”不用在本地留下中间文件tar -czf - /home/user/data | ssh userremote-server cat /backup/data.tar.gz解释一下-f -表示归档文件名为-这个特殊符号告诉tar直接写标准输出。然后管道把数据喂给ssh在远程服务器上把它写到文件里。反过来也可以ssh userremote-server tar -czf - /var/log/nginx | tar -xzvf - -C /local/backup这个命令是把远程服务器的Nginx日志目录打包后直接拉回本地解压。省去了先传到本地、再解压的中间步骤效率很高。我用这个方式迁移过多个应用目录比scp传一个包再解压要省一步而且在管道两端都可以随时插入grep或gzip处理。4. 高频问题与排查技巧4.1 解压时提示gzip: stdin: not in gzip format这个报错我见过很多次了。原因通常是文件后缀是.tar.gz但实际内容不是gzip压缩格式。可能的情况比如文件传输过程中损坏头部数据丢了。文件其实是个未压缩的tar包只是名字带了.gz后缀。有人在源端用tar -cf创建了纯tar包然后手动改名为.tar.gz。排查方法先用file命令看看真实类型file backup.tar.gz输出会是gzip compressed data、POSIX tar archive或其他。如果显示是POSIX tar archive直接用tar -xvf解压即可去掉-z参数。如果显示data说明文件头已经损坏基本没救了需要重新获取。4.2 解压出来的文件属主和权限不对tar打包时默认会保留文件的权限和属主信息但解压时能否恢复取决于解压的用户身份。root用户解压时可以恢复属主普通用户解压时属主会被强制设置为当前用户权限位保留。如果你用普通用户解压一个包含root属主文件的包就会看到所有文件都变成了自己属主权限位如果超过当前用户的权限范围解压后文件可能不可写。还有一种情况打包时使用了--no-same-owner或解压时用了--no-same-owner结果就是归档内的属主信息被忽略。跨环境迁移比如从开发机备份到生产机时我通常会在解压命令里加--no-same-owner避免把开发环境里的uid映射到生产环境造成权限混乱。4.3 归档里的文件路径带前导斜杠导致解压覆盖绝对路径上面提到过用绝对路径打包归档里的文件路径也会带前导/解压时tar默认会忽略前导斜杠把文件解到当前目录。但如果用户在归档里手动塞入了../../这类相对路径解压时可能把文件写到预期目录之外存在安全风险。高版本的GNU tar会对..路径进行拦截但保险起见解压不受信任的tar包前先执行tar -tzvf backup.tar.gz检查一下内容列表确认没有异常路径再解压。4.4 磁盘空间不足导致打包到一半失败打包一个几GB的目录时如果磁盘空间不够tar会在写文件时报错退出。解决办法是在执行之前先估计归档大小du -sh /home/user/data比如du显示4.2GB那.tar.gz压缩后通常会在1GB到3GB之间取决于文件类型。确认目标分区剩余空间df -h足够后再执行打包。另一个小技巧是分卷打包适用于归档文件超过单盘容量的场景tar -czvf - /home/user/data | split -b 2GB - archive.tar.gz.part恢复时先合并再解压cat archive.tar.gz.part* | tar -xzvf -4.5 tar: Exiting with failure status due to previous errors这个提示不是具体错误而是一个汇总信息。真正的原因在它上面的日志里。常见的是打包过程中某些文件无法读取比如文件权限不足、文件在打包过程中被删除或修改。排查思路是先看日志里有没有Permission denied或No such file or directory再决定是调整权限、清理并发操作还是给tar加--ignore-failed-read参数跳过失败文件。4.6 文件名或目录名包含空格怎么办Linux下文件名带空格很常见tar的处理方式是把整个文件名用引号包起来但归档内实际记录的是原始文件名所以不用做特殊处理。真正需要注意的是写脚本时一堆空格文件名拼接命令容易出问题尤其是配合find或其他命令输出时。我的习惯是打包前先列出文件列表确认没有多余的空格或换行影响命令解析。5. 几个我踩过的坑和实际使用心得5.1 我踩过的最大的坑解压前忘了看路径有一次我从同事那里拿了一个叫services.tar.gz的包解压完发现当前目录多了一堆散落的文件没有顶层目录。后来才知道同事在打包时用了tar -czf package.tar.gz *.conf *.service这种逐个文件打包的写法没有先包一层目录。从那以后我养成了一个习惯不管是谁的包只要不是我亲手打的拿到手先tar -tzvf看一眼内容。如果是散落的文件就在/tmp下建一个临时目录再解压确认文件名没有覆盖现有文件的风险再移到目标位置。5.2 用tar备份数据库时别忽略快照一致性如果你在MySQL或PostgreSQL正在写入时直接打包数据目录出来的备份文件可能是“脏”的——有些WAL日志还没落盘有些表文件写到一半就被tar读走了恢复时数据库可能起不来。我的做法是用数据库自带的工具导出逻辑备份mysqldump、pg_dump或者借助存储快照功能在快照层做物理备份不是在数据目录上直接跑tar。如果实在要用tar物理备份至少先把数据库服务停掉再打包或者用LOCK TABLES配合FLUSH TABLES临时锁定写操作保证归档内数据是一致的。5.3 配合crontab做定时归档时注意环境变量问题我在一个项目里用crontab每天凌晨打包日志结果第二天一看任务根本没执行成功。排查半天发现问题出在crontab的PATH环境变量太简略tar命令虽然找到了但脚本里用的gzip、xz这些外部工具不在PATH里tar内部调用压缩工具时静默失败导致生成的归档文件是未压缩的tar包体积大了一倍不止。解决办法是在脚本开头显式指定PATH或者用/usr/bin/tar这样的绝对路径调用同时把压缩工具路径也写全#!/bin/bash export PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin这一行能解决绝大多数定时任务里“命令找不到”的诡异问题。5.4 tar打包时保留相对路径和权限的最终建议做备份时我强烈建议在打包前先cd到目标目录的上级用相对路径打包这样解压后目录结构是可控的同时配合--preserve-permissions参数显式保留权限位避免某些环境下默认行为不一致cd /home/user tar -czvf project-backup.tar.gz --preserve-permissions project这样生成的包在解压时project目录及里面所有文件的权限位都会被完整还原对需要复现开发环境、迁移服务配置的场景特别有用。5.5 最后再分享一个小技巧如何快速解压多层嵌套压缩包有时候你会拿到一个.tar.gz包解压出来里面又是一个.tar.gz甚至还有一层。我遇到过一次三层嵌套的包解完一层又看到下一层。当时懒得分三次解压就写了个简单的循环检测while true; do f$(ls -1 | grep -E \.(tar\.gz|tar\.bz2|tar\.xz)$ | head -n 1) [ -z $f ] break tar -xf $f rm -f $f done这个脚本在当前目录循环寻找压缩包解压后删除原包直到没有压缩包为止。虽然场景有点刁钻但在处理一些从网上下载的多层封装包时特别省事。日常用得最多的其实还是那几条基础命令tar -czvf、tar -xzvf、tar -tzvf。把这几个记熟再配合--exclude和-C参数基本能覆盖80%的工作场景。剩下的都是遇到问题再查参数表用几次自然就记住了。
返回列表