← 返回文章
技术笔记

同一个文件,可以有两个名字

2026-09-14 · M 写

同一台机器上,一个文件可以有两个名字,而且两个名字不分主次。

$ echo hello > f
$ ln f hard
$ ls -i f hard
281474976724460 f
281474976724460 hard

两行最前面的数字是同一个 inode 编号。文件系统认的是这个编号,不是名字。ln 做的事,就是给已有的文件再登记一个名字。GNU 手册的原文是:硬链接是已有文件的另一个名字,链接与原名无法区分。

ls -l 第二列那个从 1 变成 2 的数,记的正是「这个文件现在有几个名字」。两个名字指向同一个文件,通过其中任何一个写内容,另一个读到的一样:

$ echo changed > hard
$ cat f
changed

没有原件与副本之分,权限也是同一套 —— 用其中一个名字改权限,另一个名字下一起变,因为它们本来就是同一个 inode。

cp 对照着看最清楚。两个命令各做一件事:

$ ln f hard      # 再登记一个名字
$ cp f copy      # 复制出一个新文件
$ ls -i f hard copy
281474976724466 f
281474976724466 hard
281474976724467 copy

fhard 同号,copy 是另一个号。之后改 copyf 不受影响 —— 副本从复制那一刻起就与原件无关;改 hardf 跟着变 —— 因为根本没有两个文件。需要第二个独立的文件,用 cp;需要第二个名字,用 ln

删掉一个名字,数据还在

rm 删的是名字,不是文件:

$ rm f
$ cat hard
changed

链接数从 2 降到 1,文件还在。unlink(2) 手册写明回收的时机:当这个名字是最后一个链接、且没有进程还开着这个文件时,「文件被删除,它占用的空间可以重新使用」。名字是文件被持有的方式,持有数清零,文件才消失。

把旧名字删掉再重建一个同名文件,也不会把链接接回去:

$ echo brand new > f
$ ls -i f hard
1407374883567085 f
844424930145772  hard

两个编号已经不同。hard 里还是 changed,新的 f 是另一个文件,只是恰好叫了同一个名字。名字可以被复用,文件的身份不能。

删掉一个名字之后文件仍在,是因为持有数没有清零。把最后一个名字也删掉,持有数归零,文件才从文件系统里消失。我数过:链接数 2 时删一个名字,文件还在;链接数 1 时再删,文件就没有了。rm 从头到尾做的都是同一件事 —— 把计数减一。

改文件的姿势也决定链接的生死。直接写入是原地截断,inode 不变:

$ echo one > f
$ ln f hard
$ echo two > f
$ cat hard
two

追加也一样,发生在同一个 inode 上:

$ echo four > f
$ ln f hard
$ echo five >> f
$ cat hard
four
five

而很多编辑器保存时是「另存临时文件、再改名覆盖」,那个改名等于换了一个文件:

$ echo three > g
$ mv g f
$ cat f
three
$ cat hard
one

这组对照解释了一个常见现象:用编辑器改过的文件,硬链接经常「莫名其妙地断开」。不是断开,是保存方式换掉了 inode。

反过来也可以利用这一点,做一个零成本的快照:改一个重要文件之前,先 ln 一个名字留着,改坏了随时能退回旧名字下的原内容;确认改好了,再把备份名字删掉。占用的空间为零 —— 因为自始至终只有一个文件。

目录不适用

$ mkdir d
$ ln d d2
ln: d: hard link not allowed for directory

手册的说法是「多数系统禁止给目录建硬链接」。原因在结构上:目录之间要是能互相指向,就能造出环,许多按树遍历的工具会跟着出问题。符号链接记的是路径而不是文件身份,行为与硬链接不同;这一篇只讲我实测过的这一种。

想看一个文件现在有几个名字,stat -c %h 直接给出这个数:

$ stat -c %h hard
1

参考:GNU Coreutils 手册 · lnunlink(2)。文中的编号、链接数与报错行都是实测的输出。

—— M-D