部署脚本里要把一堆文件打包传到服务器,敲命令时永远在纠结:tar -czftar -xzf 哪个是打包哪个是解包?参数顺序换一下会不会报错?打包完发现体积比预期大一倍,是哪里出了问题?

这些问题的根源都是同一件事:tar 到底在干什么,很多人从来没搞清楚过。

这是 Linux 常见命令系列第四篇详细教程,承接 Linux 常见命令与工具地图chmod/chowngrepfind

tar 是什么:打包和压缩是两件事

tar 全称 tape archive(磁带归档),这个名字暴露了它的历史——最早是为了把多个文件写进磁带这种只能顺序读写的设备设计的。它做的事情只有一件:把多个文件和目录归档成一个文件流,文件之间的边界、权限、目录结构都保留下来,但内容本身不压缩

压缩是另外一回事,靠 tar 调用外部的压缩程序(gzip、bzip2、xz)来完成。这就是为什么打包压缩的命令永远长这样:

1
2
3
4
5
tar -czf archive.tar.gz files/
# -c:创建归档(打包)
# -z:调用 gzip 压缩
# -f:指定输出文件名
# 三个动作一步做完,但概念上是两件事:先打包,再压缩

只打包不压缩,得到的是 .tar 文件;打包之后再压缩,才是 .tar.gz.tar.bz2 这些格式——文件名里的双重后缀不是巧合,就是在诚实地告诉你这个文件经过了两道工序。

核心操作模式:-c、-x、-t、-r

四个字母决定 tar 这次要做什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
tar -czf archive.tar.gz files/
# -c(create):创建一个新归档

tar -xzf archive.tar.gz
# -x(extract):把归档解出来

tar -tzf archive.tar.gz
# -t(list):只列出归档里有什么文件,不真的解压
# 解压一个来路不明的压缩包之前,先用这个看看里面到底装了什么,避免解出一堆文件把当前目录搞乱

tar -rf archive.tar files/new_file.txt
# -r(append):往已有的归档里追加文件
# 注意 -r 不能配合压缩格式使用(不能对已压缩的归档直接追加),只在处理未压缩的 .tar 文件时有效,不算高频用法

-c-x-t-r 这四个是互斥的,一条命令只会用其中一个——决定这次操作”创建””解压”还是”查看”。

压缩格式选择:-z、-j、-J

打包之后要不要压缩、用哪种压缩算法,是三个独立的选项:

1
2
3
4
5
6
7
8
tar -czf archive.tar.gz files/
# -z:gzip 压缩,速度快,压缩率一般,日常打包首选

tar -cjf archive.tar.bz2 files/
# -j:bzip2 压缩,压缩率比 gzip 更高,但压缩速度更慢

tar -cJf archive.tar.xz files/
# -J:xz 压缩,压缩率最高,速度也最慢,适合追求体积、不在乎时间的场景(比如长期归档存储)

选型建议很简单:不确定就用 -z,这是使用最广泛、兼容性最好的选择;只有明确要求”体积越小越好、时间不是问题”时才考虑 -J-j 处在中间地带,除非有历史包袱(老脚本已经在用 bzip2),日常新写的命令不太有必要专门选它。

这三个选项本质上是 tar 去调用系统里对应的外部压缩程序(gzip、bzip2、xz),不是 tar 自己内置的压缩实现——精简过的容器镜像如果没装对应的二进制,加了 -z 也会报错,排查”tar 报错找不到命令”时先确认这个压缩程序本身装了没有。

-f 参数:几乎总是需要

-f 用来指定归档文件名,几乎每条 tar 命令都会带上——不写 -f,tar 默认会尝试读写标准输入输出而不是一个文件,这在日常场景下基本不是你想要的行为:

1
2
3
4
5
tar -czf backup.tar.gz /var/www/site
# -f 后面紧跟的 backup.tar.gz 就是输出文件名
# 短选项挤在一起写时(clumped,比如 -czf),-f 必须放在最后一位——
# 它是唯一需要带参数的选项,紧跟在它后面的内容会被当成文件名;
# 写成 -zcf 会导致参数归属错乱,GNU 官方文档明确警告这可能覆盖掉重要文件,别冒险重排顺序

解压到指定目录:-C

不需要先 cd 到目标目录再解压,-C 直接指定解压目的地:

1
2
3
tar -xzf archive.tar.gz -C /opt/deploy/
# -C:解压到这个目录,不影响当前工作目录
# 等价于 cd /opt/deploy/ && tar -xzf /path/to/archive.tar.gz,但不用你手动切目录、也不用担心相对路径算错

排除文件:–exclude

打包项目目录时,通常不想把 .gitnode_modules 这类无关内容也打进去:

1
2
3
tar -czf project.tar.gz --exclude=".git" --exclude="node_modules" project/
# --exclude:排除匹配的文件或目录,可以写多个
# 打包前排除比打包后再删省事得多,尤其是 node_modules 这种体积大的目录

只解压部分内容

归档很大、只需要其中一两个文件时,不用全解出来:

1
2
3
tar -xzf archive.tar.gz path/to/specific-file.txt
# 在 -f 后面直接跟归档内的具体路径,只解出这一个文件
# 具体路径要和 tar -tzf 列出来的路径完全一致,包括开头有没有 ./ 这种细节

两个常见坑

打包时用了绝对路径,解压到别的机器上跑到意料之外的地方

1
2
3
4
5
6
7
tar -czf backup.tar.gz /home/user/project
# 打包进去的路径信息是 home/user/project(tar 默认会去掉开头的 /)
# 解压时会在当前目录下创建 home/user/project 这一整串目录结构,而不是直接解出 project 内容

cd /home/user/project && tar -czf backup.tar.gz .
# 正确做法:先 cd 进目标目录,再用相对路径(. 表示当前目录)打包
# 解压出来就是干净的文件内容,不会带着一层多余的目录结构

压缩包体积比预期大,排查是不是重复压缩了:图片、视频、已经是 .zip/.gz 格式的文件本身已经压缩过,信息熵接近上限,再用 -z 压一遍收益很小,有时因为压缩算法的元数据开销,体积甚至会比不压缩略大。如果打包对象里混了大量这类文件,可以考虑不加压缩参数、只用纯 -c 打包(.tar 而不是 .tar.gz),省下压缩耗时也不会有实质损失。

tar 常用参数速查表

参数 作用
-c 创建归档
-x 解压归档
-t 只列出归档内容,不解压
-r 追加文件到已有归档(不能用于已压缩的归档)
-z 调用 gzip 压缩/解压,速度快
-j 调用 bzip2 压缩/解压,压缩率更高但更慢
-J 调用 xz 压缩/解压,压缩率最高,最慢
-f FILE 指定归档文件名
-v 显示处理过程中的文件列表(verbose)
-C DIR 解压到指定目录
--exclude=PATTERN 排除匹配的文件或目录

tar 之外

Windows 用户对接、需要跨平台兼容性更好的场景,zip/unzip 通常比 tar 更合适——这个话题留到以后单独展开。系列下一篇计划写 rsync——增量同步、排除规则、远程传输这些参数量级比 tar 还大,够写满一整篇。