都是 inode 惹的祸

在Linux系统里有一个文件管理系统帮助用户管理磁盘、存储文件。例如 ext2, ext3, ext4 等等。每一个文件和目录,都拥有一个索引节点号,称之为 inode number。该号码用于标识文件、目录的元数据信息。比如所有权(例如有些仅为root能打开)、所有者、文件大小和创建时间、修改时间等等。

当用户存储文件的时候,可以理解为将文件"托管"给文件系统。文件系统返回一个 inode number 供用户随时查找使用。至于物理底层是用磁盘、光盘、SSD;是无备份还是有两三个拷贝;这些都是文件系统的使命。一般同一个磁盘分区上,inode number是不重复的。通过 inode 能唯一索引到一个文件或者目录。

使用 ls -i 命令可以查看当前目录下的文件 inode 数字 (第一列)

$ ls -i 
 239114 Applications
 239245 Downloads    
 239121 Desktop
 239229 Documents 

创建 符号链接 和 硬链接

符号链接硬链接 在创建时使用的命令极为相似。但是两者有本质上的区别。

符号链接本质上是一个特殊文本文件。它具备自己的 inode 编号。它的文本内容包含了一个路径,指向一个目标文件 (具备另一个 inode 编号)。相当于一个快捷方式。删除该快捷方式对于目标文件毫无影响。同时,目标文件即可以存在,也可以不存在。若不存在的话,这个符号链接就是一个破碎的、死链接。

举例说明,我们先创建一个普通文件:my-file.txt

$ touch my-file.txt
$ echo abcdefg > my-file.txt # 填充几个字符进入文件
$ ls -i
6985592 my-file.txt

采用 ln -s 为这个文件添加一个符号链接(快捷方式)

$ ln -s my-file.txt my-symbolic-link.txt
$ ls -i
6985592 my-file.txt
6986078 my-symbolic-link.txt

可见快捷方式本身与目标文件采用了不同的 inode 编号 (69855926986078),也就是两者本质是不同的文件。

硬链接就更加底层。它指向了文件系统中的 同一个实体文件/目录。与符号链接不同,硬链接没有指向其他路径的概念。举个例子,当你存储了一份文件:my-file.txt,可将其硬链接于 my-hard-link.txt 位置。此时文件系统并不会存储两份数据,而仅有一份数据。两者共享同一个 inode 编号。

举例说明,接上回,采用 ln 创建硬链接

$ ln my-file.txt my-hard-link.txt
$ ls -i
6985592 my-file.txt
6985592 my-hard-link.txt
6986078 my-symbolic-link.txt

可见 my-file.txtmy-symbolic-link.txt 根本是同一个文件,共享唯一索引编号 6985592

这里有两点需要说明:

  • 符号链接可以跨文件系统(甚至跨网络文件系统);但硬链接不能,只局限于本文件系统;
  • 硬链接包含一个隐形的计数。当该计数归零时,该份数据被文件系统认为是不再被索引,可放心删除。(参看下文)

查看 符号链接 与 硬链接 ls -l

采用 ls -i 能查看到 inode 编号。采用 ls -l 可直观地查看符号链接和硬链接.

$ ls -l
-rw-r--r--  2 jack  staff   8 Aug 14 14:40 my-file.txt
-rw-r--r--  2 jack  staff   8 Aug 14 14:40 my-hard-link.txt
lrwxr-xr-x  1 jack  staff  11 Aug 14 14:43 my-symbolic-link.txt -> my-file.txt

在第二列,2 2 1 数字代表了硬链接的计数。其中 my-file.txt 因为与 my-hard-link.txt 硬链接,所以计数为 2my-symbolic-link.txt因为是自己一个独立文件,所以计数为 1

注意到第三行,my-symbolic-link.txt 指向了 my-file.txt.

如果我们继续添加 my-file.txt 的文件内容:

$ echo 123456 >> my-file.txt
$ ls -l
-rw-r--r--  2 jack  staff  15 Aug 14 15:02 my-file.txt
-rw-r--r--  2 jack  staff  15 Aug 14 15:02 my-hard-link.txt
lrwxr-xr-x  1 jack  staff  11 Aug 14 14:43 my-symbolic-link.txt -> my-file.txt

原始文件外,其硬链接的数据会产生同步变化。但符号链接不变。第四列从原本的 8 8 11 变化为了 15 15 11。这里的数字代表了文件体积,也就是 byte。

如果我们删除硬链接的两个文件中的一个,则隐形计数会 -1

$ rm my-hard-link.txt
$ ls -l
-rw-r--r--  1 jack  staff  15 Aug 14 15:02 my-file.txt
lrwxr-xr-x  1 jack  staff  11 Aug 14 14:43 my-symbolic-link.txt -> my-file.txt

注意到第二列不再是 2 2 1 而是 1 1

我编辑的到底是快捷方式还是文件?

如果使用 vim 编辑 硬链接 就不用说了,本身就是原始文件。

如果使用 vim 编辑一个 符号链接 文件,你实际上会编辑链接指向的原始文件,而不是链接本身。

当你打开一个符号链接文件并对其进行编辑时,vim 会自动跟随符号链接并打开链接指向的原始文件。

这是因为符号链接只是一个指向另一个文件或目录的路径,它本身并不包含文件的内容。因此,任何对符号链接文件的编辑实际上都会直接修改链接指向的原始文件。

需要注意的是,如果你想要修改符号链接本身,你需要使用特定的命令(如 ln -sf)来更改链接的目标路径。这将更新链接的指向并创建一个新的符号链接。

查找硬链接的两个方法

能够通过 ls -l 看见隐形计数,知道被硬链接了几次的确很酷。但是若不在同一目录下,则需要费一番功夫才能找到其他的硬链接,尤其当一个文件有十几上百次被不同地方硬链接过。

介绍两个实用命令来达到这个目的:

第一个命令: find ~/ -samefile my-file.txt

这里我们筛查 ~/ 目录下的所有文件,这个文件必须与 my-file.txt 是同一文件(也就是共享了 inode 编号)

第二个命令:

$ ls -i my-file.txt
$ find ~/ -inum <inode_number>

同样是筛查 ~/ 目录下的所有文件,这次我们分两步,第一步取得 my-file.txt 的 inode 编号,再用 inode 编号来进行精确匹配查找。