> For the complete documentation index, see [llms.txt](https://man.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://man.bsdcn.org/man7/symlink.7.md).

# symlink(7)

`symlink` — 符号链接处理

## 名称

`symlink`

## 符号链接处理

符号链接是指向其他文件的文件。要理解它们的行为，必须先了解硬链接的工作原理。指向某个文件的硬链接与原始文件无法区分，因为它是对原始文件名所对应对象的引用。对文件的修改与引用该文件所用的名称无关。硬链接不能指向目录，也不能引用不同文件系统上的文件。符号链接包含它所链接到的文件的名称，也就是说，它是指向另一个名称的指针，而不是指向底层对象。因此，符号链接可以引用目录，也可以跨越文件系统。

由于符号链接及其引用的对象共存于文件系统命名空间中，在区分链接本身和被引用对象时可能会产生混淆。从历史上看，命令和系统调用以 somewhat ad-hoc 的方式采用了各自的链接跟随约定。这里概述了本系统中实现的更统一的方法的规则。本地应用程序也遵循这些规则非常重要，这样用户界面才能尽可能保持一致。

对符号链接的处理方式有两种：要么对链接本身进行操作，要么对链接所引用的对象进行操作。在后一种情况下，称应用程序或系统调用“跟随”（follow）该链接。符号链接可以引用其他符号链接，此时将逐级解引用，直到找到非符号链接的对象、找到引用了不存在文件的符号链接，或者检测到循环。循环检测通过设置可跟随链接数的上限来实现，如果超过此限制则会报错。

有四个独立的方面需要讨论。它们是：

* 作为系统调用文件名参数的符号链接。
* 用于忽略符号链接的挂载选项。
* 作为命令行参数指定给不遍历文件树的实用程序的符号链接。
* 遍历文件树的实用程序所遇到的符号链接，无论是在命令行中指定的，还是在文件层次结构遍历过程中遇到的。

### 系统调用

第一个方面是作为系统调用文件名参数的符号链接。

除下文所述例外，所有系统调用都跟随符号链接。例如，如果存在一个指向名为“`afile`”的文件的符号链接“`slink`”，则系统调用“`open("slink" ...)`”将返回指向文件“afile”的文件描述符。

有十三个系统调用不跟随链接，而是对符号链接本身进行操作。它们是：lchflags(2)、lchmod(2)、lchown(2)、lpathconf(2)、lstat(2)、lutimes(2)、[readlink(2)](/sys/readlink.2.md)、readlinkat(2)、[rename(2)](/sys/rename.2.md)、renameat(2)、[rmdir(2)](/sys/rmdir.2.md)、[unlink(2)](/sys/unlink.2.md) 和 unlinkat(2)。由于 [remove(3)](/stdio/remove.3.md) 是 [unlink(2)](/sys/unlink.2.md) 的别名，它也不跟随符号链接。当 [rmdir(2)](/sys/rmdir.2.md) 或带 `AT_REMOVEDIR` 标志的 unlinkat(2) 应用于符号链接时，将失败并返回 Er ENOTDIR 错误。

linkat(2) 系统调用不跟随符号链接，除非给定 `AT_SYMLINK_FOLLOW` 标志。

以下系统调用跟随符号链接，除非给定 `AT_SYMLINK_NOFOLLOW` 标志：chflagsat(2)、faccessat(2)、fchmodat(2)、fchownat(2)、fstatat(2) 和 [utimensat(2)](/sys/utimensat.2.md)。

可以通过 lchown(2) 系统调用更改现有符号链接的所有者和组。可以通过 lchflags(2)、lchmod(2)、lchown(2) 和 lutimes(2) 系统调用分别更改现有符号链接的标志、访问权限、所有者/组和修改时间。其中，只有标志和所有权被系统使用；访问权限会被忽略。

4.4BSD 系统与历史上的 4BSD 系统的不同之处在于，系统调用 [chown(2)](/sys/chown.2.md) 已改为跟随符号链接。lchown(2) 系统调用是在新的 [chown(2)](/sys/chown.2.md) 的局限性显现后添加的。

### 挂载选项

FreeBSD 有一个 [mount(8)](/man8/mount.8.md) 选项 `nosymfollow`。启用此选项时，内核不会跟随所挂载文件系统上的符号链接，并返回 Er EACCES 。你仍然可以创建或删除符号链接，或者读取符号链接的值。

此选项适用于从不可信的外部存储系统挂载文件系统，或挂载公开可写的 **`/tmp`** 文件系统时使用，以防止基于符号链接的权限提升和沙箱逃逸攻击。

挂载选项 `nosymfollow` 首次出现于 FreeBSD 3.0。

### 不遍历文件树的命令

第二个方面是作为命令行文件名参数指定给不遍历文件树的命令的符号链接。

除下文所述例外，命令会跟随作为命令行参数命名的符号链接。例如，如果存在一个指向名为“`afile`”的文件的符号链接“`slink`”，则命令“`cat slink`”将显示文件“`afile`”的内容。

重要的是要认识到，此规则包括可能选择性地遍历文件树的命令，例如命令“`chown file`”包含在此规则中，而命令“`chown -R file`”则不包含。后者在下面的第三个方面中描述。

如果明确意图让命令对符号链接本身进行操作而不是跟随符号链接，例如希望“`chown slink`”更改“`slink`”所指向的文件的所有权（无论它是否为符号链接），则应使用 `-h` 选项。在上面的例子中，“`chown root slink`”将更改“`slink`”所引用文件的所有权，而“`chown -h root slink`”将更改“`slink`”本身的所有权。

此规则有五个例外。[mv(1)](/man1/mv.1.md) 和 [rm(1)](/man1/rm.1.md) 命令不跟随作为参数命名的符号链接，而是分别尝试重命名和删除它们。注意，如果符号链接通过相对路径引用文件，将其移动到另一个目录很可能会导致它停止工作，因为路径可能不再正确。

[ls(1)](/man1/ls.1.md) 命令也是此规则的例外。为了与历史系统兼容（当 [ls(1)](/man1/ls.1.md) 不进行树遍历时，即未指定 `-` 选项时），如果指定了 `-H` 或 `-L` 选项，或者未指定 `-F`、`-d` 或 `-l` 选项，[ls(1)](/man1/ls.1.md) 命令会跟随作为参数命名的符号链接。[ls(1)](/man1/ls.1.md) 是唯一一个即使不进行文件树遍历，`-H` 和 `-L` 选项也会影响其行为的命令。

file(1) 和 [stat(1)](/man1/stat.1.md) 命令也是此规则的例外。这些命令默认不跟随作为参数命名的符号链接，但如果指定了 `-L` 选项，则会跟随。

4.4BSD 系统与历史上的 4BSD 系统的不同之处在于 **chown** 和 **chgrp** 命令会跟随命令行中指定的符号链接。

### 遍历文件树的命令

以下命令要么选择性地、要么总是遍历文件树：chflags(1)、[chgrp(1)](/man1/chgrp.1.md)、[chmod(1)](/man1/chmod.1.md)、[cp(1)](/man1/cp.1.md)、[du(1)](/man1/du.1.md)、[find(1)](/man1/find.1.md)、[ls(1)](/man1/ls.1.md)、[pax(1)](/man1/pax.1.md)、[rm(1)](/man1/rm.1.md)、[tar(1)](/man1/bsdtar.1.md) 和 [chown(8)](/man8/chown.8.md)。

重要的是要认识到，以下规则同样适用于文件树遍历过程中遇到的符号链接以及作为命令行参数列出的符号链接。

第一条规则适用于引用非目录类型文件的符号链接。对符号链接适用的操作会在链接本身上执行，否则链接将被忽略。

命令“`rm -r slink directory`”将删除“`slink`”，以及在“`directory`”的树遍历中遇到的所有符号链接，因为符号链接可以被删除。[rm(1)](/man1/rm.1.md) 在任何情况下都不会以任何方式影响“`slink`”所引用的文件。

第二条规则适用于引用目录类型文件的符号链接。引用目录类型文件的符号链接默认情况下永远不会被“跟随”。这通常被称为“物理”遍历，与引用目录的符号链接会被跟随的“逻辑”遍历相对。

尽可能一致地，通过指定 `-H`（“半逻辑”）标志，可以让执行文件树遍历的命令跟随命令行中命名的所有符号链接，无论它们引用的文件类型如何。此标志旨在使命令行命名空间看起来像逻辑命名空间。注意，对于并非总是进行文件树遍历的命令，如果未同时指定 `-` 标志，`-H` 标志将被忽略。

例如，命令“`chown -HR user slink`”将遍历以“`slink`”所指向文件为根的文件层次结构。注意，`-H` 与前面讨论的 `-h` 标志不同。`-H` 标志使命令行中指定的符号链接在要执行的操作和树遍历两方面都被解引用，就好像用户指定了符号链接所指向的文件的名称一样。

尽可能一致地，通过指定 `-L`（“逻辑”）标志，可以让执行文件树遍历的命令跟随命令行中命名的所有符号链接，以及在遍历过程中遇到的所有符号链接，无论它们引用的文件类型如何。此标志旨在使整个命名空间看起来像逻辑命名空间。注意，对于并非总是进行文件树遍历的命令，如果未同时指定 `-` 标志，`-L` 标志将被忽略。

例如，命令“`chown -LR user slink`”将更改“`slink`”所引用文件的所有者。如果“`slink`”引用的是目录，chown(1) 将遍历以其引用目录为根的文件层次结构。此外，如果 chown(1) 遍历的任何文件树中遇到任何符号链接，它们将以与“`slink`”相同的方式处理。

尽可能一致地，可以通过指定 `-P`（“物理”）标志来指定默认行为。此标志旨在使整个命名空间看起来像物理命名空间。

对于默认情况下不进行文件树遍历的命令，如果未同时指定 `-` 标志，`-H`、`-L` 和 `-P` 标志将被忽略。此外，可以多次指定 `-H`、`-L` 和 `-P` 选项；最后指定的那个决定命令的行为。这旨在允许你将命令别名为以某种方式行为，然后在命令行上覆盖该行为。

[ls(1)](/man1/ls.1.md) 和 [rm(1)](/man1/rm.1.md) 命令对这些规则有例外。[rm(1)](/man1/rm.1.md) 命令对符号链接本身进行操作，而不是它引用的文件，因此从不跟随符号链接。[rm(1)](/man1/rm.1.md) 命令不支持 `-H`、`-L` 或 `-P` 选项。

为了与历史系统保持兼容，[ls(1)](/man1/ls.1.md) 命令的行为略有不同。如果不指定 `-F`、`-d` 或 `-l` 选项，[ls(1)](/man1/ls.1.md) 将跟随命令行中指定的符号链接。如果指定了 `-L` 标志，[ls(1)](/man1/ls.1.md) 将跟随所有符号链接，无论其类型如何，无论是在命令行中指定的还是在树遍历中遇到的。

## 参见

chflags(1), [chgrp(1)](/man1/chgrp.1.md), [chmod(1)](/man1/chmod.1.md), [cp(1)](/man1/cp.1.md), [du(1)](/man1/du.1.md), [find(1)](/man1/find.1.md), [ln(1)](/man1/ln.1.md), [ls(1)](/man1/ls.1.md), [mv(1)](/man1/mv.1.md), [pax(1)](/man1/pax.1.md), [rm(1)](/man1/rm.1.md), [tar(1)](/man1/bsdtar.1.md), lchflags(2), lchmod(2), lchown(2), lstat(2), lutimes(2), [readlink(2)](/sys/readlink.2.md), [rename(2)](/sys/rename.2.md), [symlink(2)](/sys/symlink.2.md), [unlink(2)](/sys/unlink.2.md), [fts(3)](/gen/fts.3.md), [remove(3)](/stdio/remove.3.md), [chown(8)](/man8/chown.8.md), [mount(8)](/man8/mount.8.md)
