前两天我在重新编译一版 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 管道粘起来。
1tar 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,过来搭把手。
两个工具,一根管道,四十五年。

