☰
Linux tar命令完全指南:从底层原理到打包压缩解压实战
2026/10/14 4:01:05 网站建设 项目流程

Linux 下处理文件的打包、压缩、解压,tar 命令是绕不开的第一个门槛。我当年第一次在服务器上要迁移几个网站目录时,搜到的教程几乎都是同一句:tar -czvf backup.tar.gz /data/www,背下来照抄能跑,可等我真的需要“只要压缩包里的某一个配置文件”“打包时排除日志目录”“把归档直接送到远程机器”时,才发现自己根本不理解 tar 在干什么。这篇稿子想把 tar 从底层工作方式到高频实战一次讲透,帮那些已经能敲出 tar 命令但遇到变化就卡壳的读者补上漏掉的那块拼图。文章里的命令都来自真实使用场景,每个报错都实际踩过,你可以照着抄,也可以只看思路。

1. 先把“打包”和“压缩”这两件事拆开看

1.1 tar 的本职其实不是压缩

在 Linux 里习惯叫“tar 压缩”,但从设计上讲,tar 的本职是归档:把一堆文件和目录拼成一个连续的文件流。它最初为磁带设计,磁带是顺序存储设备,适合一次性写入大块数据,于是 tar 将多个文件按“文件头 + 内容”的结构串联起来,方便磁带顺序读写。压缩能力是后来挂载上去的外援:-z 调用 gzip,-j 调用 bzip2,-J 调用 xz,由外部压缩程序处理这个连续流。

这个区别重要吗?非常。它决定了你记命令的方式:tar 常见的“打包不压缩”场景如tar -cf archive.tar file1 file2,产生的 .tar 文件,体积约等于原来所有文件之和,只是合并成单个文件便于传输。要减小体积才需要引入压缩参数。很多人把压缩率不佳归咎于 tar,其实 tar 只是个搬运工,选对压缩算法才是省空间的关键。

打个生活化的比方:tar 像把散落茶几上的杂物扔进一个大收纳箱,压缩算法像用真空泵把箱子里的空气抽走。收纳箱负责合并,抽真空负责减体积,两回事。

1.2 从 -cvf 到 -czvf,参数是怎么叠加出压缩能力的

常见写法是tar -czvf backup.tar.gz /data/www。拆开看:

  • c(create)创建归档
  • z(gzip)调用 gzip 压缩
  • v(verbose)显示处理过程
  • f(file)紧跟归档文件名

注意 f 的位置:f 后面必须马上跟文件名,所以惯例写成tar -czvf,f 放最后一个字母。写成tar -cvfz backup.tar.gz会把 f 后面跟的“z”当成归档文件名,逻辑就错了。这条看起来很小,却是新手最容易原地懵掉的地方。

如果只打包不压缩,把 z 去掉即可:tar -cvf backup.tar /data/www。如果需要 bzip2 或 xz,分别用-j和-J。参数可以自由组合,核心没变:c / f 决定归档动作,z / j / J 决定压缩外援。

新版 GNU tar 还有一个-a(auto-compress)参数,按归档文件名后缀自动选择压缩程序,比如tar -caf backup.tar.zst /data就不用手动加 --zstd。省心,但我在脚本里还是会写全参数,因为不同机器的 tar 版本对 zstd 支持不同,明确写法出问题更容易定位。

1.3 看到 tar 包,先做后缀侦查

拿到压缩包先看后缀,养成肌肉记忆:

后缀压缩算法解压参数打包参数
.tar无tar -xftar -cf
.tar.gz / .tgzgziptar -xzftar -czf
.tar.bz2bzip2tar -xjftar -cjf
.tar.xzxztar -xJftar -cJf

实际上从 GNU tar 1.27.1 开始,解压时即使不写 z / j / J,它也会自动探测压缩格式,比如tar -xf xx.tar.gz也能解。依赖自动识别在大多数机器上没问题,但老系统的 tar 版本不一定支持,而且在脚本里少写一个字母,等别人接手时看半天才明白。我的建议是:交互操作可以偷懒,写文档和脚本务必把参数写全,让命令自解释。

另外一个实用技巧:不确定包是什么类型,先跑file backup.tar.gz。输出会直接告诉你gzip compressed data还是bzip2 compressed data。严格来说 file 比后缀更可靠,因为重命名或传输损坏后后缀可能骗人。

2. 解压的正确姿势:别急着一把梭

2.1 解压前先侦察:文件类型与包内结构

见过太多人拿到压缩包直接tar -xzf,解完发现文件散了一地,或者发现是一个内层嵌套目录,或者覆盖了同名文件。冷静三秒,先跑两条命令:

file app-2024.tar.gz tar -tf app-2024.tar.gz | head -n 20

file 确认压缩格式,tar -tf 列出归档成员而不解压。重点看包内是否有一个清晰的顶层目录。比如输出以app-2024/开头,那解压就会生成一个顶层文件夹;如果输出一堆config.xml、readme.txt平铺在根,没有任何共同前缀,解压就会把文件直接扔进当前目录。后者解压时最好先 mkdir 一个目录再 cd 进去,或者用 -C 指定。

还要留意有没有可疑路径:成员名里出现/etc绝对路径、或../这种向上跳转。现代 tar 默认会剥离首字符/并拒绝明显危险的路径穿越,但拿到陌生包先看一眼结构永远不会错。

2.2 按需解压:只要某几个文件或某个子目录

完整压缩包几十 GB,只想要里面一个配置文件,不需要全解。tar 支持直接指定成员名抽取:

tar -xzf app-2024.tar.gz app-2024/config/app.conf

这样只解出app-2024/config/app.conf这一个文件,其父目录会自动创建。指定成员名的路径必须以包内结构为准,用tar -tf查看到的路径原样抄。如果只想抽出某个子目录:

tar -xzf app-2024.tar.gz app-2024/config

如果目录里有十几个文件,又不想挨个列名,用通配符:

tar -xzf app-2024.tar.gz --wildcards 'app-2024/docs/*.md'

关键点:通配符模式一定要加引号。不加引号 shell 会提前把它展开成当前目录下的匹配结果,匹配不到就变成一个空参数,行为立刻走样。我在这上面交过学费。

按需解压配合-O可以把文件内容直接输出到标准输出,遇到想快速看某个配置内容时非常舒服:

tar -xzf app-2024.tar.gz -O app-2024/config/app.conf

注意 -O 解压的是“文件”不是目录,它把内容打到屏幕而不是落到磁盘,适合临时排查。

2.3 解压到指定目录、剥离层级、覆盖规则

常用组合:

  • -C 目标目录:把文件解到指定目录,目录必须已存在
  • --strip-components=1:去掉归档路径的顶层目录。这招特别适合解压到一个已经按项目名建好的目录:mkdir new && tar -xzf app-2024.tar.gz -C new --strip-components=1,得到的是 new 下的全部内容,而不是 new/app-2024/
  • -k:遇到已有文件不覆盖;默认是直接覆盖。批量更新版本时,想保留本地改过的内容可以用 -k
  • -p或--same-owner:解压时保留权限与属主。非 root 用户解压 root 打的包通常无法真正做到,root 用户默认会保留。多机迁移场景记得主动加上

这里有一个细节:tar 默认解压时会尝试恢复 mtime 等时间戳,如果你只是临时解压看内容,看到文件显示“昨天的时间”是正常的,不是解压坏了。

3. 打包时的决策:压缩算法、排除项与权限保留

3.1 gzip、bzip2、xz 到底怎么选

打包不只是敲一条 tar。先想清楚压缩目标:这是一份要经常传输的临时备份,还是长期留存的基本不再动的归档?

算法速度压缩率适合场景打包参数
gzip快中日常传输、临时备份-czf
bzip2较慢较高文本、源码归档-cjf
xz很慢最高冷备份、日志归档-cJf
zstd很快接近 gzip 略优现代系统、大批量文件--zstd

没有人规定必须用 gzip。比如一个纯日志目录,历史文件不再增长,我通常用 xz,压完体积能小到 gzip 的三分之一,省下来的磁盘是真金白银。如果是业务频繁用的备份,比如每天凌晨的增量包,xz 压缩时间成本太高,gzip 更快更稳。

zstd 是近些年的新选择,压缩速度非常快,压缩比接近 gzip 甚至略好,适合那种需要频繁打包又不想等太久的场景。但使用前确认系统里的 tar 编译了 zstd 支持,RedHat 系较新版本没问题,老版本可能没有。

3.2 排除文件怎么写才不误伤

目标目录里几十 GB 的 node_modules、build、缓存目录,打包时必须排掉。tar 提供--exclude=模式,可以写多次:

tar -czf app-release.tar.gz --exclude='*/node_modules' \ --exclude='*/build' --exclude='*.log' app-2024/

几个关键认知:

  • 排除模式匹配的是包内相对路径,不是打包命令里的源路径。比如你打的是/data/app-2024,排除模式写app-2024/logs还是*/logs要看包的顶层结构,用tar -tf生成的列表作参考最不会错。
  • 想“排除目录内容但保留目录本身”,用--exclude='cache/*'而不是--exclude='cache'。前者只排掉 cache 下的文件,归档里仍有一个空 cache 目录;后者连目录也整个消失。有些程序依赖空目录存在,这个差别很实用。
  • 多个排除项建议写进一个文件,用-X 文件导入,脚本里避免命令过长,一行一个模式,注释友好。

平时我最常打的组合就是:排除 node_modules、排除 .git、排除临时文件。写完可以用tar -tf快速检查里面还有没有漏网进去的大目录,比直接解压验证省事得多。

3.3 权限、属主和软链接的保留

默认情况下 tar 会保存权限位;打包和解压两边都是 root 时,属主也会保留。但日常打包时容易忽略软链接。

  • 目录里有软链接指向某处的文件,默认 tar 存的是链接本身,解压后还是软链接,指向同一目标。这通常是我们想要的。
  • 如果希望把链接指向的内容真正复制进归档,加--dereference(或 -h),归档体积会变大,但换来了独立可用性。

还有一个“前置”的坑:打包时如果整个源目录正在被写入(比如数据库数据目录),tar 解出来的可能是写了一半的不一致内容。不是 tar 的问题,是对源的一致性要求。重要业务数据备份前要么停写、要么用文件系统快照,别指望 tar 同时解决快照问题。

打包完成后建议做两件事:gzip -t backup.tar.gz(或tar -tf backup.tar.gz > /dev/null)验证归档可读;再sha256sum backup.tar.gz记录摘要,之后传输或恢复时对一下,能确认文件没有被截断。

4. 进阶场景:tar 不落盘网络传输与增量备份

4.1 一条 tar 管道直接搬到远程

需要迁移目录到另一台机器,没空间放中间归档文件怎么办?tar 的输出可以写到 stdout,再用 ssh 管道接过去:

tar -czf - /data/app-2024 | ssh user@remote 'tar -xzf - -C /opt'

解释一下:tar -czf -中 -f - 的意思是“归档写到标准输出”,这是 tar 的惯用法,代表 stdout。管道交给 ssh,远程命令再tar -xzf -从 stdin 解压,-C 落到目标目录。如果两端都希望保留权限,加上 -p:tar -czpf - ...和远程tar -xzpf -。

好处是全程不生成中间文件,省磁盘,也避免了本地 archive 残留。缺点是中间任何一端带宽抖动,管道断了再次重来。迁移小到中等体量目录时,这个方案非常爽。如果是批量几 TB 的数据,还是应该用 rsync 或者先落盘再校验,更可控。

注意:远程命令的-C /opt目录必须存在;另外 ssh 两侧的引号别弄丢,最好先手动找一个目录测试一次。

4.2 配合 find 实现按时间增量归档

tar 自身没有“只打包比某时间新的文件”的直接参数(新版 tar 支持--newer-mtime,但文件列表复杂时还是 find 更灵活)。最常见的是:

find /data/app -type f -newermt '2024-06-01 00:00:00' -print0 | \ tar --null -czf incremental-2024-06.tar.gz -T -
  • find 输出满足条件的文件列表,交给-T -读入归档名单。-T - 表示从标准输入读取文件列表
  • -print0和--null成对出现,按空字符而不是换行分隔文件名。目录或文件名里有空格时,这项组合是保命选项
  • 注意 find 输出的是“文件”,递归过程中目录结构会随文件路径保留,但空的子目录不会出现在归档里

这个方案适合给大型业务目录做定期补充备份:本周只打包本周变动的文件。成本低、速度快,缺点是恢复时需要与上一份全量归档配合,不能单独依赖。如果你只是想找出“最近 7 天修改的文件”做快速打包,把 newermt 换成-mtime -7即可。

4.3 大归档分割与完整性校验

大归档想拆成多个小包,配合 split:

tar -czf - /data/big | split -b 2G - backup_part_

生成 backup_part_aa、backup_part_ab 等 2GB 分卷。恢复时:

cat backup_part_* | tar -xzf -

也可以直接cat backup_part_* > combined.tar.gz再按普通流程解压。

分卷最大的风险:某一段文件损坏会导致整个归档解压失败,而且经常是解到一半才报错,误以为前面几十 GB 白解了。所以大归档我很少用 split 分卷,更推荐整体归档后做两个校验动作:

gzip -t backup.tar.gz && tar -tf backup.tar.gz > /dev/null sha256sum backup.tar.gz

gzip -t 专门做完整性检测,能发现流中间的截断或坏块;tar -tf 则验证归档结构能否正常遍历。两者都过了,归档基本可信。sha256 摘要单独写在旁边,传输到异地后再算一次比较。

5. 踩过的坑与最后的习惯建议

5.1 高频报错排查链路

日常运维里 tar 的报错翻来覆去就那几条,我按实际遇到频率列一下处理思路。

  1. tar: Removing leading '/' from member names——不是错误,是 tar 在安全化路径,把绝对路径转成相对路径,防止解压覆盖系统根目录。看到它说明打包时用了绝对路径。真的想保留绝对路径(我不推荐),加 -P。

  2. gzip: stdin: not in gzip format——最有迷惑性。文件后缀是 .tar.gz,但实际内容不是 gzip 流,常见于下载未完成、文件被截断、或者重命名了非 gzip 文件。排查链路:file xxx.tar.gz看真实类型;ls -l看体积是否明显偏小;如果是从网络传输来的,重新获取。

  3. tar: Cannot open: No such file or directory——解压到不存在的 -C 目录,或者按需解压时指定的成员名与实际包内路径对不上。处理链路:先tar -tf精确复制路径字符串,再确认目标目录已 mkdir。

  4. tar: Exiting with failure status due to previous errors——这是“上游有错误,我退出了”的总结信息。重点看它上面的第一个具体错误,比如file changed as we read it(打包期间源文件被修改)、权限不足、磁盘满。排查链路:用df -h检查文件系统,用tail看日志头部的时间点是否和打包时间冲突。

  5. tar: write error——基本是磁盘满了或者管道对端中断。先清理空间,再重试。

一个通用排查顺序:file 确认类型 → tar -tf 确认结构 → 单条命令最小化复现 → 加 -v 看卡在哪一步。这套思路能解决绝大多数 tar 问题。

5.2 我推荐的一套稳定命令组合

日常我最常用的三件套:

# 打包(放到 /tmp,避免半成品污染目标目录) tar -czf /tmp/backup.tar.gz -C /data/app-2024 \ --exclude='*/node_modules' --exclude='*.log' . # 检查结构 tar -tf /tmp/backup.tar.gz | head -n 10 gzip -t /tmp/backup.tar.gz # 解压(先建目录再进) mkdir /opt/app-2024-new && tar -xzf /tmp/backup.tar.gz -C /opt/app-2024-new

有个不算冷的知识:打包时尽量到目录外面用-C 目录 .的写法,归档内容以.为根,路径干净,恢复时指哪打哪。直接用tar -czf /tmp/x.tar.gz /data/app-2024打出来的包,恢复时会自动重建data/app-2024这串层级的目录。如果恢复机器上目录结构不一样,会出现“解压出来路径不对”的困扰。

5.3 几个能救命的小习惯

最后根据我的经验总结几个小习惯,都是吃过亏换来的:

  • 任何陌生包,先 file,再 tar -tf,再决定怎么处理,这三步总共花不到五秒
  • 打包重要数据,一定要验证后再删除源文件。很多事故出在“备份完了,把源删了,才发现备份包打不开”
  • 写脚本里用set -o pipefail(bash)配合管道:tar 和 gzip 同一条管道时,前面 tar 挂了,管道默认可能“看起来成功”。加 pipefail 才能让脚本准确报错
  • 归档文件名带日期:backup-2024-06-01.tar.gz 而不是 backup.tar.gz。一个月后你看着一整排同名文件会感谢自己
  • 用--exclude排除大目录之后,最后以du -sh对照归档体积,确认排除生效,别等恢复时才发现打进去一个几十 G 的缓存

我在实际使用中还有一个感受:tar 命令参数多,但真正高频的就那么十几个组合,完全没必要把 man 手册背下来。先把这个“归档 + 压缩”的心智模型建立起来,再遇到新需求时查一下对应参数,基本就能一次用对。这套路径我已经走了很多年,可以放心直接从第一条命令抄走。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询