只是想知道为什么如果我使用rm -rf my-symlink
它只会删除符号链接,但rm -rf my-symlink/
会删除链接目录中的文件并保留符号链接?
答案1
stat my-symlink
通过比较和的输出,您可以看到差异stat my-symlink/
。my-symlink
,不带斜杠,是符号链接本身;my-symlink/
带斜线的 是符号链接指向的目录,您可以通过比较my-symlink/
其指向的目录的 inode 和 来单独验证该目录。
有了这些信息,您所看到的行为就与中描述的相符rm
的规格:处理符号链接时,rm
如果链接指向目录,则删除该链接而不向下“进入”它;当处理目录(使用选项-r
)时,它会递归删除其内容。在这种my-symlink/
情况下,rm
确实尝试删除“目录”,但失败了,因为它不是目录而是符号链接 - 但是由于该-f
标志,这不会导致错误。
答案2
我想我应该进一步调查一下这种行为,所以这是另一个答案。
在内部,rm
使用FTS递归到文件层次结构。fts_open
将路径数组作为参数,并为每个路径创建一个树结构。这使得程序员能够无缝地探索多个位置,就好像它们是一个统一层次结构的一部分一样。
这是一个测试程序,您可以用它来自己玩 FTS。
#include <stdio.h>
#include <stdlib.h>
#include <fts.h>
int main(int argc, char* argv[])
{
if(argc < 2) return EXIT_FAILURE;
char* const* arr = argv + 1;
FTS* hier = fts_open(arr, FTS_NOSTAT | FTS_PHYSICAL, NULL);
FTSENT* ent;
while((ent = fts_read(hier))) {
printf("%s info=%d (D=%d DP=%d F=%d SL=%d)\n",
ent->fts_accpath, ent->fts_info,
ent->fts_info == FTS_D, ent->fts_info == FTS_DP,
ent->fts_info == FTS_F || ent->fts_info == FTS_NSOK,
ent->fts_info == FTS_SL);
}
fts_close(hier);
return EXIT_SUCCESS;
}
假设我们已经创建了以下目录结构:
$ mkdir dir
$ touch dir/file
$ ln -s dir sym
现在,让我们考虑您的第一个案例,看看 FTS 如何引领探索。
$ gcc fts.c
$ ./a.out sym
sym info=12 (D=0 DP=0 F=0 SL=1)
正如您所看到的,在本例中,sym
被视为一个文件。更准确地说,是符号链接。有了这些信息,rm
就可以将其视为文件,并调用unlinkat(AT_FDCWD, "sym", 0)
.最后一个参数 (0) 导致unlinkat
行为类似于unlink
。换句话说:它只是删除一个文件。结果,您的链接消失了。
现在,让我们看看 发生了什么sym/
。
$ ./a.out sym/
sym/ info=1 (D=1 DP=0 F=0 SL=0)
file info=11 (D=0 DP=0 F=1 SL=0)
sym/ info=6 (D=0 DP=1 F=0 SL=0)
在本例中,sym
被视为其目标目录。我们首先迭代sym
,然后sym/file
再次sym
迭代。最后一个是由于 FTS 的工作方式决定的:首先,它迭代内容,然后返回根节点。这对于 来说其实是相当方便的rm
。在第一遍 ( D
) 中,它可以删除文件,在第二遍 ( DP
) 中删除空目录。
正如您所看到的,在本例中,FTSsym/
在这两种情况下都报告为目录。这是因为我们给路径添加了尾部斜杠,这强制内核将其解释为目录。对于链接来说,这意味着无论如何它都会跟随它。从更技术的角度来说,规格说明:
至少包含一个非斜杠字符且以一个或多个尾部斜杠结尾的路径名应像在路径名中附加一个点字符 ( '.' ) 一样进行解析。
由于 FTS 报告sym/
为目录,rm
因此其行为就像删除空目录一样。因此,它调用unlinkat(AT_FDCWD, "sym/", AT_REMOVEDIR)
.这会导致unlinkat
行为类似于rmdir
.
然而,在解析sym/
路径后,unlinkat
系统调用将意识到实际上并没有给它一个目录。因此它将报告ENOTDIR
,这会触发:
$ rm: cannot remove ‘sym/’: Not a directory
实际上,如果您-f
从调用中删除该标志...这正是您将看到的内容。现在,这是一个错误还是一个功能......我不知道。