我正在创建一个 shell 脚本,它将获取文件的文件名/路径并确定该文件是符号链接还是硬链接。
唯一的问题是,我不知道如何查看它们是否是硬链接。我创建了 2 个文件,一个是硬链接,一个是符号链接,用作测试文件。但是我如何确定一个文件是 shell 脚本中的硬链接还是符号呢?
另外,如何找到符号链接的目标分区?假设我有一个链接到不同分区的文件,我如何找到该原始文件的路径?
答案1
吉姆的回答解释了如何测试符号链接:通过使用test
's-L
测试。
但严格来说,测试“硬链接”并不是您想要的。硬链接的工作原理取决于 Unix 处理文件的方式:每个文件由一个 inode 表示。那么单个inode有零个或多个名字或者目录条目或者,从技术上来说,硬链接(你所说的“文件”)。
值得庆幸的是,该stat
命令(如果可用)可以告诉您一个索引节点有多少个名称。
所以你正在寻找这样的东西(这里假设 GNU 或 busybox 实现stat
):
if [ "$(stat -c %h -- "$file")" -gt 1 ]; then
echo "File has more than one name."
fi
该-c '%h'
位指示stat
仅输出到 inode 的硬链接数量,即文件具有的名称数量。-gt 1
然后检查是否大于 1。
请注意,符号链接就像任何其他文件一样,也可以链接到多个目录,因此您可以对一个符号链接有多个硬链接。
答案2
一个例子:
$ touch f1
$ ln f1 f2
$ ln f1 f3
$ ln -s f1 s1
$ ln -s f2 s2
$ ln -s ./././f3 s3
$ ln -s s3 s4
$ ln s4 s5
$ ls -li
total 0
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 f1
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 f2
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 f3
10802345 lrwxrwxrwx 1 stephane stephane 2 Nov 12 19:56 s1 -> f1
10802346 lrwxrwxrwx 1 stephane stephane 2 Nov 12 19:56 s2 -> f2
10802347 lrwxrwxrwx 1 stephane stephane 8 Nov 12 19:56 s3 -> ./././f3
10802384 lrwxrwxrwx 2 stephane stephane 2 Nov 12 19:56 s4 -> s3
10802384 lrwxrwxrwx 2 stephane stephane 2 Nov 12 19:56 s5 -> s3
f1
、f2
和目录条目f3
是同一个文件(相同的 inode:10802124,您会注意到链接是 3)。它们是相同的硬链接常规的文件。
s4
也是s5
相同的文件 (10802384)。他们属于类型符号链接, 不是常规的。他们指向一条路,就在这里s3
。由于s4
和s5
是同一目录的条目,因此s3
两者的相对路径都指向同一文件(inod 10802347 的文件)。
如果你执行ls -Ll
,即在解析符号链接后要求获取文件信息:
$ ls -lLi
total 0
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 f1
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 f2
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 f3
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 s1
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 s2
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 s3
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 s4
10802124 -rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 s5
你会发现它们全部解决到同一文件(10802124)。
您可以检查文件是否是符号链接[ -L file ]
。同样,您可以使用 测试文件是否是常规文件[ -f file ]
,但在这种情况下,检查是在解析符号链接后完成的。
硬链接不是一种文件类型,它们只是文件(任何类型)的不同名称。
您可以通过检查文件的链接计数来检查文件是否多次(硬)链接到一个或多个目录
POSIXly:
has_more_than_one_link() (
export LC_ALL=C # only locale where the output of ls -n is specified
export TZ=UTC0 # simplify the mtime field (which we don't need anyway)
# generation as an optimisation.
unset -v IFS # for default separator for read to include space and
# tab, the only 2 "blank" characters in the C/POSIX locale
ls -nqd -- "$1" | {
read -r ignore links ignore &&
[ "$links" -gt 0 ]
}
)
或者与zsh
:
has_more_than_one_link() () (($#)) $1(Nl+1)
其中函数体是一个匿名函数,其主体为(($#))
(如果参数数量 > 0,则返回 true),参数是$1(Nl+1)
通配符的扩展,当$1
且仅当该文件的链接数大于 1 时,它会扩展为 。
或者使用它的stat
内置:
zmodload zsh/stat
has_more_than_one_link() {
stat -LA2 +nlink -- "$1" && (( $2 > 1 ))
}
(这里存储链接的数量$2
以避免必须声明一个单独的变量当地的)。
有些系统也有一个外部stat
命令(通常晚于 中的命令zsh
),尽管它们彼此之间以及来自 的接口都有不同的接口zsh
。
使用 的 GNU 实现stat
,您需要处理一个-
专门调用的文件,因为 GNU将其解释为在 stdin 上stat
执行操作的含义:fstat()
has_more_than_one_link() {
local nlink
[ "$1" != - ] || set ./-
nlink=$(stat -c %h -- "$1") &&
[ "$nlink" -gt 1 ]
}
(这里假设一个类似 POSIX 的 shell 支持 local
作用域)。
答案3
使用命令的-h
和运算符:-L
test
-h file
true if file is a symbolic link
-L file
true if file is a symbolic link
http://www.mkssoftware.com/docs/man1/test.1.asp
根据这个所以线程,它们具有相同的行为,但-L
更受青睐。
答案4
这里有很多非常正确的答案,但我认为没有人真正解决了最初的误解。最初的问题基本上是“当我创建符号链接时,之后很容易识别它。但我不知道如何识别硬链接。”是的,答案基本上可以归结为“你不能”,并且或多或少地解释了原因,但似乎没有人承认,实际上,这是令人困惑和奇怪的。
如果您正在阅读所有这些内容并且已经弄清楚发生了什么,那么您就很好了;你不需要读我的一点点。如果你仍然感到困惑,那就继续吧。
真正简短的答案是,硬链接根本不是真正的链接,不像符号链接那样。它是目录结构中的一个新条目,指向与原始目录条目相同的字节串,一旦创建它,它就与第一个条目一样“真实”和合法。每一个驱动器上的“正常”文件至少有一个硬链接;没有它,你就不会看到它任何目录,并且无法引用或使用它。因此,如果您有一个文件 Fred.txt,并且将 Wilma.txt 和 Barney.txt 硬链接到该文件,则所有三个名称(和目录条目)都引用同一个文件,并且它们都同等有效。操作系统无法判断其中一个条目是在文本编辑器中单击“保存”时创建的,而其他条目是使用“ln”命令创建的。
操作系统做不过,必须跟踪有多少不同的条目指向同一文件。如果您删除 Wilma.txt,您不会释放驱动器上的任何空间,这并不奇怪。但是,如果您删除 Fred.txt(“原始”文件),您仍然不会释放驱动器上的任何空间,因为驱动器上名为 Fred.txt 的数据仍然是 Barney.txt。仅当您删除时全部目录条目的数量将使操作系统取消分配数据本身占用的空间。
如果 Barney.txt 是符号链接,则删除 Fred.txt会已取消分配空间,Barney.txt 现在将是一个损坏的链接。另外,如果您移动或重命名具有指向该文件的符号链接的文件,则会破坏该链接。但是,您可以随意移动或重命名硬链接文件,而不会破坏指向该文件/数据的其他目录条目,因为它们都是引用驱动器上同一数据块的目录条目(通过使用该数据的索引节点号)。
[两年后,最后有点困惑我等一下,我想我会澄清一下。如果您输入“mv ./Wilma.txt ../elsewhere/Betty.txt”,看起来您正在移动该文件,但实际上并非如此。您真正要做的是从当前目录的目录列表中删除一个行项目,该项目显示“名称‘Wilma.txt’与可以使用 inode ###### 找到的数据相关联” #,”,并向目录 ../elsewhere 的目录列表中添加一个新行项目,其中显示“名称‘Betty.txt’与可通过 inode ####### 找到的数据相关联”。这就是为什么您可以像移动 2 KB 文件一样快速地“移动”2 GB 文件,只要您将它们移动到同一驱动器上的另一个位置即可。]
因为操作系统必须跟踪有多少不同的目录条目指向同一数据块,所以您能判断某个特定文件是否已硬链接,即使您无法确定您正在查看的目录条目是否是“原始”目录条目。一种方法是“ls”命令,特别是“ls -l”(即破折号后的小写 L)
借用之前的例子......
-rw-r--r-- 3 stephane stephane 0 Nov 12 19:55 f1
第一个字母是破折号,所以它不是一个目录或其他奇怪的东西,它是一个“常规”普通文件。但如果它真的很普通,那么 rwx-ish 部分后面的数字将是“1”,就像“有一个目录条目指向这一数据块”一样。但它是硬链接演示的一部分,因此它显示“3”。
请注意,这可能会导致奇怪和神秘的行为(如果您没有认真考虑硬链接)。如果您在文本编辑器中打开 Fred.txt 并进行一些更改,您会在 Wilma.txt 和 Barney.txt 中看到相同的更改吗?或许。大概。如果您的文本编辑器通过打开原始文件并将更改写入其中来保存更改,那么是的,所有三个名称仍将指向相同的(新更改的)文本。但是,如果您的文本编辑器创建一个新文件 (Fred-new-temp.txt),将更改后的版本写入该文件,然后删除 Fred.txt,然后将 Fred-new-temp.txt 重命名为 Fred.txt,Wilma 和 Barney 将仍然指向原始版本,而不是新更改的版本。如果您不理解硬链接,这可能会让您有点生气。 :) [好吧,我个人实际上并不知道任何文本编辑器这会做新文件/重命名的事情,但我确实知道很多其他程序正是这样做的,所以保持警惕。]
最后一点:“fsck”(文件系统检查)检查的内容之一是驱动器上是否存在某些目录条目不再引用的数据块。有时会出现问题,指向索引节点的唯一目录条目会被删除,但驱动器空间本身不会被标记为“可用”。因此,fsck 的工作之一是将所有分配的空间与所有目录条目进行匹配,以确保不存在任何未引用的文件。如果找到一些,它会创建新的目录条目并将它们放入“lost+found”中。