前两天我重新编译了一版 nginx。
流程基本闭着眼睛都能走。wget 拉下来一个 nginx-1.27.4.tar.gz , tar -xzvf 解开,进目录, ./configure , make , make install 。
敲完 tar 那条命令的一瞬间,我突然愣了一下。
这个 .tar.gz ,两个后缀。
我用了大概十几年,从来没认真想过为什么要两个。
顺着这事我又想起来,我这双手对 tar 的记忆完全是肌肉记忆, xzvf 这四个字母该按什么顺序,大脑是不参与的,手指自己会动。正因为是肌肉记忆,我还踩过一个经典坑,偶尔下载到没有顶层目录的 tar 包,照样一梭子解下去,几十个文件啪一下全炸到当前目录,瞬间把工作目录变成垃圾场。
那种时候我一边 ls | xargs rm 一边告诉自己下次一定先 mkdir 。
但下次还是不会。
人对每天都在用的东西是最没好奇心的。
说回 .tar.gz 。这两个后缀到底是什么关系,为什么非得拆成两个。
我带着这个疑问查了一圈,发现这事比我想象的有趣得多。它不是什么历史包袱,也不是约定俗成的命名习惯,它是两个时代、两个工具掰着手指头拼出来的一个结果。
先说 tar。
tar 这个命令,最早出现在 1979 年 1 月的 Unix Version 7 里,AT&T 贝尔实验室做的。名字也没什么加密的缩写,就是 Tape ARchive。翻译过来,磁带归档。字面意思。
你可以想象一下 1979 年。那会儿大家备份数据靠的是磁带,就是老电影里那种两个圆盘转啊转的大盘子,数据一圈一圈顺序刻在带子上。你不能跳着读,也不能中间插一段,只能从头到尾一次性过。

tar 这个工具的出生使命,就是为了给磁带服务的。
所以它做的事情特别简单,就一个动作:把一堆小文件按顺序拼成一条长长的字节流,好让磁带机一口气写下去。
注意,我说的是「拼成一条流」。
tar 压根就不压缩。
你拿 tar 把一个 100MB 的目录打成一个包,出来的文件还是 100MB,一个字节都没省。它只是把这些东西排成一列而已,没做别的事情。
tar 文件内部的最小单位是 512 字节一块,这个数字也不是随便选的。它就是 Unix V7 文件系统磁盘扇区的大小。1979 年的一个选择,刻在所有 tar 文件的基因里,一直刻到今天。所以 tar 管的事情,就到「拼」为止。
那压缩呢?
tar 不管。
这事儿一直到 1992 年才有人接手。
那一年,两个叫 Jean-loup Gailly 和 Mark Adler 的人做了一个东西叫 gzip。Gailly 写压缩,Adler 写解压,1992 年 10 月 31 号,gzip 0.1 正式发布。
这里有个背景值得一提。在 gzip 之前,Unix 自带的压缩工具叫 compress,用的是 LZW 算法。但 LZW 被 Unisys 和 IBM 捏在手里,九十年代初开始到处收钱,开源圈被整得很紧张。gzip 就是被逼出来的替代品,用的是另一个叫 DEFLATE 的算法,完美绕开了专利地雷。
这段往事本身就够写一篇文章的,但今天我们只关心一个点。
gzip 做的事也特别简单,就一个动作:把一条字节流压缩成一条更短的字节流。
你注意到了吗?gzip 也很轴。它只认「一条流」。你不能拿 gzip 去压一个目录,它不认识目录这回事。给它一个文件,它就吐一个压缩后的文件;给它一条流,它就吐一条压缩后的流。别的事它一概不管。
tar 只打包不压缩,gzip 只压缩不打包。
两个工具,谁都不会干对方的活。
那怎么办?
中间用一根 Unix 管道粘起来。
tar cf - mydir | gzip > mydir.tar.gz

这条命令左边 tar 把 mydir 打成一条流,用 - 表示「别写文件,直接吐到标准输出」。中间一个 | ,把这条流接到 gzip 的嘴边。右边 gzip 啃完这条流,吐出一条压缩过的流,重定向到 mydir.tar.gz 。
一个 | 符号,两个工具,一条流水线。
你看到的那个 .tar.gz ,就是这条流水线走完之后自然掉下来的结果。它的后缀之所以是两个,是因为这个文件真的经历了两道工序。第一道叫 tar,第二道叫 gz。后缀诚实地告诉你它是谁。
这里顺便说一句 tar 后来的 -z 参数。
GNU tar 的作者们觉得每次写管道太烦,加了一个 z 参数,告诉 tar,你要压缩的时候帮我顺手调一下 gzip。这就是为什么我们今天敲 tar -xzvf 这个 z 。但你要知道,是 tar 在「替你调 gzip」,不是 tar 自己会压缩。四十五年过去了,tar 本体始终没长出压缩这项能力。
这个决定非常 Unix,非常硬核。
有意思的是,几乎就在 tar 过着自己小日子的同一年代,大洋对岸的 DOS/Windows 世界走了完全不同的一条路。
1989 年,美国程序员 Phil Katz 在 DOS 上搞出 PKZIP,顺手发明了 .zip 格式。zip 跟 tar + gzip 的哲学几乎是正相反的,一个工具同时做两件事,既打包又压缩,一把梭到底。
四年之后的 1993 年,另一个年轻人进场了。俄罗斯程序员 Eugene Roshal 发布命令行 RAR,1995 年又推出图形界面版 WinRAR。RAR 是 Roshal Archive 的缩写,字面意思就是「Roshal 的归档」,一个用作者自己名字命名的格式,主打比 zip 更高的压缩率。
WinRAR 这个软件顺便还成了互联网历史上最著名的「永恒试用版」。它写着 40 天试用期,但过期后不会拦你,只会弹一个很客气的提示让你买 license,你关掉它接着用,下次再弹。这个 40 天续命了二十多年,全球无数人用了一辈子没付过钱,它也不生气。这事本身已经成了一个互联网梗。

这一路跟 Unix 那边的区别非常明显,一个软件管到底,打包压缩全包在一个 exe 里。后来 1999 年俄罗斯人 Igor Pavlov 又搞了开源的 7-Zip,自带一个 .7z 格式,压缩率再往上提一截。1996 年 Julian Seward 做的 bzip2、2009 年 Lasse Collin 做的 xz 也都先后加入了这个江湖,压缩格式的主要玩家基本就是这些人了。
但你注意到一个很好玩的分化没有?
bzip2 和 xz 这两个后来的家伙,在 Unix 世界里叫什么? .tar.bz2 和 .tar.xz 。tar 先把文件拼成流,bzip2 或者 xz 接着压。换压缩工具可以,换哲学不行。Unix 那边认死一件事,打包和压缩必须是两件事,永远是两件事。
Windows 那边正相反,出了更强的算法,就把旧软件整个换掉,一个 exe 管到底。
这已经不只是格式之争了,是两个世界对「一个工具应该长成什么样」的根本分歧。
为什么 Unix 那边坚持要拆?
这就得搬出 Doug McIlroy 了。
1978 年,贝尔实验室的 McIlroy 在 Bell System Technical Journal 上写了一段后来被反复引用的话,核心就三句。
写程序要让它们只做一件事,并且把这件事做好。
写程序要让它们互相协作。
处理的东西最好是文本流,因为那是通用接口。
这就是后来被叫作 Unix 哲学的那一段。
但这段话最有意思的一点,不在文字本身。在于说这话的人是谁。
Doug McIlroy 不光是写这段话的人,他同时还是 Unix 管道 | 这个符号的发明者。

你品一下这个巧合。
他说「让程序互相协作」的时候,他心里想的不是什么抽象的合作,他想的就是管道。一个程序的输出,通过一根 | ,无缝流进下一个程序的嘴里。小工具、单一职责、用管道粘起来,组合出任意复杂的流水线。
.tar.gz 就是这段话最直白的产物。
tar 是第一个只做一件事的小工具,gzip 是第二个只做一件事的小工具,中间那根 | 是 McIlroy 本人发明的管道。你每打一个 .tar.gz 文件,都是在重演一遍 1978 年那段话。
这就是为什么我说它是活化石。
化石这个词听起来像是死掉的东西,但 .tar.gz 不是。你今天去 GitHub 上随便点一个 C 项目的 release,下载下来的几乎永远是 .tar.gz ,不是 .zip 。Linux 内核、nginx、curl、redis,全都是。它不是因为惯性才活着,它是因为那套哲学在今天依然被认为是对的才活着。
所以回到开头那个问题。
为什么 .tar.gz 要两个后缀?
因为它不是一个文件,是两个工具排成的一条流水线,每个后缀对应一个工位。你看到的是这条流水线吐出来的结果。
下次你再敲 tar -xzvf some-thing.tar.gz 的时候,可以稍微多看两眼那个 z 。
那个 z 不是 tar 在解压。
那是 tar 在回头喊一声 gzip,过来搭把手。
两个工具,一根管道,四十五年。

如果你也喜欢琢磨这些技术历史背后的设计哲学,或者想找更多同好交流有意思的技术话题,欢迎来 云栈社区 逛逛。这里聚集了不少对底层原理、运维脚本和命令行工具感兴趣的程序员,氛围不错。