ln 命令详解与参数实测(Ubuntu 22.04)

GNU ln 参数实测:硬链接与符号链接的同 inode/悬空对照、-s/-f/-b/-i/-v/-n 逐项行为与链接建进目录的经典坑,Ubuntu 22.04 实测。

Linux命令Linux系统UbuntuUbuntu 22.04命令行文件管理2026/10/15

ln 命令详解与参数实测(Ubuntu 22.04)

目标:全面掌握 GNU ln 的两种链接形态与常用参数——硬链接(同 inode、链接数、删原文件仍可读)、符号链接(独立 inode、可指向目录、删原文件悬空),以及 -s/-f/-b/-i/-v/-n 逐项行为与"链接建进目录里"的经典坑,全部命令与回显已在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。

一、环境信息

本篇所有命令与回显均来自同一台 Ubuntu 22.04 服务器的实测,演示数据统一放在 /root/demo/ln/ 下,读者在任意 Ubuntu 22.04 机器上按相同步骤均可复现。

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • ln 版本:GNU coreutils 8.32(ln --version 实测)
  • ln 属于 coreutils 基础工具集,Ubuntu 22.04 默认预装,无需额外安装
  • 演示目录:/root/demo/ln/
  • 测试数据:origin.txt(原始文件)、realdir/(真实目录,供目录链接实验)

二、命令概览

2.1 语法与测试数据准备

ln 的语法共四种形态(摘自帮助 Usage 行),核心两种:ln TARGET LINK_NAME 为 TARGET 创建名为 LINK_NAME 的硬链接(默认),ln -s TARGET LINK_NAME 创建符号链接。帮助原文同时强调:默认情况下目标链接名不应已存在;建硬链接时 TARGET 必须存在,符号链接则可以写任意文本(悬空软链因此合法)。

准备演示数据:

mkdir -p /root/demo/ln/realdir
cat > /root/demo/ln/origin.txt <<'EOF'
origin content
EOF

验证数据就绪:

$ ls -l /root/demo/ln
total 8
-rw-r--r-- 1 root root   15 Oct 15 06:35 origin.txt
drwxr-xr-x 2 root root 4096 Oct 15 06:35 realdir

一个文件加一个空目录,后文所有链接实验围绕它们展开。

2.2 --help 全量回显

ln --help 输出全部参数清单(实测原样保留,可作为参数速查):

Usage: ln [OPTION]... [-T] TARGET LINK_NAME
  or:  ln [OPTION]... TARGET
  or:  ln [OPTION]... TARGET... DIRECTORY
  or:  ln [OPTION]... -t DIRECTORY TARGET...
In the 1st form, create a link to TARGET with the name LINK_NAME.
In the 2nd form, create a link to TARGET in the current directory.
In the 3rd and 4th forms, create links to each TARGET in DIRECTORY.
Create hard links by default, symbolic links with --symbolic.
By default, each destination (name of new link) should not already exist.
When creating hard links, each TARGET must exist.  Symbolic links
can hold arbitrary text; if later resolved, a relative link is
interpreted in relation to its parent directory.

Mandatory arguments to long options are mandatory for short options too.
      --backup[=CONTROL]      make a backup of each existing destination file
  -b                          like --backup but does not accept an argument
  -d, -F, --directory         allow the superuser to attempt to hard link
                                directories (note: will probably fail due to
                                system restrictions, even for the superuser)
  -f, --force                 remove existing destination files
  -i, --interactive           prompt whether to remove destinations
  -L, --logical               dereference TARGETs that are symbolic links
  -n, --no-dereference        treat LINK_NAME as a normal file if
                                it is a symbolic link to a directory
  -P, --physical              make hard links directly to symbolic links
  -r, --relative              create symbolic links relative to link location
  -s, --symbolic              make symbolic links instead of hard links
  -S, --suffix=SUFFIX         override the usual backup suffix
  -t, --target-directory=DIRECTORY  specify the DIRECTORY in which to create
                                the links
  -T, --no-target-directory   treat LINK_NAME as a normal file always
  -v, --verbose               print name of each linked file
      --help     display this help and exit
      --version  output version information and exit

The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.
The version control method may be selected via the --backup option or through
the VERSION_CONTROL environment variable.  Here are the values:

  none, off       never make backups (even if --backup is given)
  numbered, t     make numbered backups
  existing, nil   numbered if numbered backups exist, simple otherwise
  simple, never   always make simple backups

Using -s ignores -L and -P.  Otherwise, the last option specified controls
behavior when a TARGET is a symbolic link, defaulting to -P.

GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Report any translation bugs to <https://translationproject.org/team/>
Full documentation <https://www.gnu.org/software/coreutils/ln>
or available locally via: info '(coreutils) ln invocation'

💡 说明:-n(--no-dereference)的说明值得特别记住——"treat LINK_NAME as a normal file if it is a symbolic link to a directory"。不加它时,若链接名本身是个指向目录的软链,ln 会把新链接建进那个目录里(3.11 有完整实测与绕坑方法)。

2.3 版本确认

$ ln --version | head -1
ln (GNU coreutils) 8.32

三、参数逐项实测

本章先实测硬链接与符号链接两种形态,再逐个参数给出「命令 + 实测回显 + 说明」,所有回显与服务器日志逐字一致;演示文件全部位于 /root/demo/ln/。

3.1 硬链接:同一个 inode 的第二个名字

不带参数的 ln 创建硬链接。用 ls -li 与 stat 验证 inode 与链接数:

$ ln /root/demo/ln/origin.txt /root/demo/ln/hard.txt && ls -li /root/demo/ln
total 12
31616888 -rw-r--r-- 2 root root   15 Oct 15 06:35 hard.txt
31616888 -rw-r--r-- 2 root root   15 Oct 15 06:35 origin.txt
31616886 drwxr-xr-x 2 root root 4096 Oct 15 06:35 realdir
$ stat -c 'inode=%i links=%h name=%n' /root/demo/ln/origin.txt /root/demo/ln/hard.txt
inode=31616888 links=2 name=/root/demo/ln/origin.txt
inode=31616888 links=2 name=/root/demo/ln/hard.txt

两个名字的 inode 同为 31616888、权限位后的链接数从 1 变成 2——磁盘上仍是那一份数据,hard.txt 只是同一个 inode 的第二个目录项。硬链接没有"原件/副本"之分,两个名字地位完全对等。

3.2 硬链接共享数据:改一个等于改全部

通过 hard.txt 追加一行,再读 origin.txt 验证:

$ printf 'appended via hard\n' >> /root/demo/ln/hard.txt && cat /root/demo/ln/origin.txt
origin content
appended via hard

通过任何一个名字写入,另一个名字立刻读到——因为本就是同一份数据。这也意味着"删掉一个名字"只是链接数减一,数据要等所有名字都被删除才释放。

3.3 符号链接:独立 inode 的路径记录

ln -s 创建符号链接(软链)。同样用 ls -li 观察:

$ ln -s /root/demo/ln/origin.txt /root/demo/ln/soft.txt && ls -li /root/demo/ln
total 12
31616888 -rw-r--r-- 2 root root   33 Oct 15 06:35 hard.txt
31616888 -rw-r--r-- 2 root root   33 Oct 15 06:35 origin.txt
31616886 drwxr-xr-x 2 root root 4096 Oct 15 06:35 realdir
31616889 lrwxrwxrwx 1 root root   24 Oct 15 06:35 soft.txt -> /root/demo/ln/origin.txt
$ readlink /root/demo/ln/soft.txt
/root/demo/ln/origin.txt

soft.txt 是全新的 inode 31616889、类型为 l、大小 24 字节——正好是记录的目标路径串长度,链接数保持 1。它不是数据本身,只是一条"去哪找数据"的路径记录。

💡 技术拓展:链接有两种实现,特性差异直接决定选型——

维度硬链接符号链接
inode与原文件相同独立新 inode
跨文件系统不支持(必须同文件系统)支持(存的是路径文本)
目录支持不允许(内核限制,防环)支持(ls 目标目录畅通)
原文件删除后数据仍在,链接照常可读悬空,访问报 No such file or directory
空间占用仅新增一个目录项链接文件自身(存路径串,几十字节起)

本文两种都做了完整实测(硬链见 3.1、软链见 3.3);日常给目录做入口、跨盘引用用软链,给大文件做免拷贝副本用硬链。

3.4 删除原文件:硬链接照常可读,软链悬空

删除原始文件 origin.txt,观察两种链接的不同命运:

$ rm /root/demo/ln/origin.txt
$ stat -c 'inode=%i links=%h name=%n' /root/demo/ln/hard.txt
inode=31616888 links=1 name=/root/demo/ln/hard.txt
$ cat /root/demo/ln/hard.txt
origin content
appended via hard
$ cat /root/demo/ln/soft.txt; echo exit=$?
cat: /root/demo/ln/soft.txt: No such file or directory
exit=1

硬链接 hard.txt 的链接数降回 1 但数据完好(inode 还有人引用);软链 soft.txt 记录的路径已失效,读取报 No such file or directory、退出码 1——这就是"悬空软链"。要确认一个软链是否悬空,用 ls -l(显示为指向不存在目标的链接)或直接 cat 试读。

3.5 指向目录的软链可行,目录不能建硬链接

软链可以指向目录,访问行为与真目录一致:

$ ln -s /root/demo/ln/realdir /root/demo/ln/dirlink && ls -ld /root/demo/ln/realdir /root/demo/ln/dirlink
lrwxrwxrwx 1 root root   21 Oct 15 06:35 /root/demo/ln/dirlink -> /root/demo/ln/realdir
drwxr-xr-x 2 root root 4096 Oct 15 06:35 /root/demo/ln/realdir
$ ls /root/demo/ln/dirlink
$ readlink /root/demo/ln/dirlink
/root/demo/ln/realdir

ls dirlink 空输出(目录为空)说明透过软链正常进入了 realdir;readlink 能随时读出它记录的目标。反过来,给目录建硬链接被内核直接拒绝:

$ ln /root/demo/ln/realdir /root/demo/ln/dirhard; echo exit=$?
ln: /root/demo/ln/realdir: hard link not allowed for directory
exit=1

目录硬链接会制造环(.. 语义混乱、遍历无法终止),文件系统层直接禁止,即使 root 也不行。

3.6 悬空软链可以直接创建

软链创建时不检查目标是否存在,指向不存在路径的链接照样建成功:

$ ln -s /root/demo/ln/nowhere.txt /root/demo/ln/dangling.txt && ls -l /root/demo/ln/dangling.txt
lrwxrwxrwx 1 root root 25 Oct 15 06:35 /root/demo/ln/dangling.txt -> /root/demo/ln/nowhere.txt

dangling.txt 建立成功(ls -l 只显示记录值),直到真正访问它才会报错(如 3.4 所示)。这个特性常用于"目标稍后才出现"的场景(如服务启动前的占位链接)。

3.7 -f:覆盖已存在的链接名

链接名已存在时,默认拒绝创建;-f(--force)先移除旧链接名再建:

$ ln /root/demo/ln/hard.txt /root/demo/ln/lnk.txt && stat -c 'inode=%i links=%h name=%n' /root/demo/ln/hard.txt /root/demo/ln/lnk.txt
inode=31616888 links=2 name=/root/demo/ln/hard.txt
inode=31616888 links=2 name=/root/demo/ln/lnk.txt
$ ln /root/demo/ln/hard.txt /root/demo/ln/lnk.txt; echo exit=$?
ln: failed to create hard link '/root/demo/ln/lnk.txt': File exists
exit=1
$ ln -f /root/demo/ln/hard.txt /root/demo/ln/lnk.txt; echo exit=$?
exit=0

第二个名字 lnk.txt 已存在时再次创建报 File exists 退出码 1;ln -f 则替换成功。对软链的覆盖见 3.8(配合 -b 还能留底)。

3.8 -b:改指向前备份旧链接

-b(--backup 无参形态)把已存在的链接名先改名为 原名~ 再建新链接。实测把指向文件的软链 blink2.txt 改指到目录 realdir:

$ ln -s /root/demo/ln/hard.txt /root/demo/ln/blink2.txt
$ ln -b -f -s /root/demo/ln/realdir /root/demo/ln/blink2.txt && ls -l /root/demo/ln | grep blink2
lrwxrwxrwx 1 root root   21 Oct 15 06:39 blink2.txt -> /root/demo/ln/realdir
lrwxrwxrwx 1 root root   22 Oct 15 06:39 blink2.txt~ -> /root/demo/ln/hard.txt

blink2.txt 已指向 realdir,旧指向(hard.txt)完整保留在 blink2.txt~ 里。-b -f 组合是"更换链接指向且可回滚"的标准写法。

3.9 -i:替换已有链接名前交互确认

-i(--interactive)在链接名已存在时询问是否替换。回答 n 则不动、退出码 0:

$ ln -s /root/demo/ln/hard.txt /root/demo/ln/ilink2.txt
$ printf 'n\n' | ln -i -s /root/demo/ln/realdir /root/demo/ln/ilink2.txt; echo exit=$?
ln: replace '/root/demo/ln/ilink2.txt'? exit=0
$ readlink /root/demo/ln/ilink2.txt
/root/demo/ln/hard.txt

回答 y 则替换为新指向:

$ printf 'y\n' | ln -i -s /root/demo/ln/realdir /root/demo/ln/ilink2.txt; echo exit=$?
ln: replace '/root/demo/ln/ilink2.txt'? exit=0
$ readlink /root/demo/ln/ilink2.txt
/root/demo/ln/realdir

两次 readlink 前后对照:指向从 hard.txt 变为 realdir。人工维护链接时用 -i 防误覆盖,脚本里用 -f(或 -b -f 留底)表达策略。

3.10 -v:显示创建过程(硬链与软链格式不同)

-v(--verbose)打印每个新建的链接,注意硬链用 =>、软链用 ->:

$ ln -v /root/demo/ln/hard.txt /root/demo/ln/vhard.txt
'/root/demo/ln/vhard.txt' => '/root/demo/ln/hard.txt'
$ ln -s -v /root/demo/ln/realdir /root/demo/ln/vsoft.txt
'/root/demo/ln/vsoft.txt' -> '/root/demo/ln/realdir'

两种箭头恰好呼应了两种链接的本质:硬链是"两个名字等价(=>)",软链是"一条指向记录(->)"。

3.11 -n 与经典坑:链接名指向目录时,链接建进了目录里

当 LINK_NAME 本身是指向目录的软链时,ln 默认会解引用它,把新链接建进那个目录里——这是 ln 最著名的坑。第一轮实测就踩到过:对指向 realdir 的软链 ilink.txt 执行 ln -i -s,询问的替换对象竟变成了目录内的文件:

$ printf 'n\n' | ln -i -s /root/demo/ln/hard.txt /root/demo/ln/ilink.txt; echo exit=$?
ln: replace '/root/demo/ln/ilink.txt/hard.txt'? exit=0

注意询问路径是 /root/demo/ln/ilink.txt/hard.txt——ilink.txt 被当成了目录。用干净的例子完整复现这个过程:

$ mkdir -p /root/demo/ln/newdir
$ ln -s /root/demo/ln/newdir /root/demo/ln/dlink
$ ls /root/demo/ln/newdir
$ ln -s /root/demo/ln/hard.txt /root/demo/ln/dlink
$ ls -l /root/demo/ln/newdir
total 0
lrwxrwxrwx 1 root root 22 Oct 15 06:39 hard.txt -> /root/demo/ln/hard.txt

预想中"把 dlink 改指向 hard.txt"的执行结果是:newdir 里凭空多了一个链接(dlink 被解引用成了 newdir)。加 -n(--no-dereference)把链接名当普通文件对待,配合 -f 即可正确替换:

$ ln -s -n -f /root/demo/ln/realdir /root/demo/ln/dlink
$ readlink /root/demo/ln/dlink
/root/demo/ln/realdir

dlink 现在真正指向 realdir。清理实验残留:

$ rm /root/demo/ln/newdir/hard.txt && rmdir /root/demo/ln/newdir

结论:凡是"替换一个指向目录的软链"的场景,一律带上 -n;许多脚本模板里的 ln -sfn 就是 -s -f -n 的合写。

四、实战示例

4.1 给大文件建免拷贝副本

硬链接零冗余,适合日志、数据集这类"多处引用同一份内容":

$ ln /root/demo/ln/origin.txt /root/demo/ln/hard.txt && ls -li /root/demo/ln
total 12
31616888 -rw-r--r-- 2 root root   15 Oct 15 06:35 hard.txt
31616888 -rw-r--r-- 2 root root   15 Oct 15 06:35 origin.txt
31616886 drwxr-xr-x 2 root root 4096 Oct 15 06:35 realdir
$ stat -c 'inode=%i links=%h name=%n' /root/demo/ln/origin.txt /root/demo/ln/hard.txt
inode=31616888 links=2 name=/root/demo/ln/origin.txt
inode=31616888 links=2 name=/root/demo/ln/hard.txt

同 inode、链接数 2,磁盘上只有一份数据(语义详解见 3.1、3.2)。

4.2 给目录提供稳定入口

用软链给真实目录做一个短入口,路径解引用与读链都正常:

$ ln -s /root/demo/ln/realdir /root/demo/ln/dirlink && ls -ld /root/demo/ln/realdir /root/demo/ln/dirlink
lrwxrwxrwx 1 root root   21 Oct 15 06:35 /root/demo/ln/dirlink -> /root/demo/ln/realdir
drwxr-xr-x 2 root root 4096 Oct 15 06:35 /root/demo/ln/realdir

真实目录搬家时只需重建这一条软链,上游引用路径不用改(目录不能建硬链接的限制见 3.5)。

4.3 更换软链指向并保留旧指向

-b -f -s 三连:备份旧链接、强制覆盖、创建软链:

$ ln -s /root/demo/ln/hard.txt /root/demo/ln/blink2.txt
$ ln -b -f -s /root/demo/ln/realdir /root/demo/ln/blink2.txt && ls -l /root/demo/ln | grep blink2
lrwxrwxrwx 1 root root   21 Oct 15 06:39 blink2.txt -> /root/demo/ln/realdir
lrwxrwxrwx 1 root root   22 Oct 15 06:39 blink2.txt~ -> /root/demo/ln/hard.txt

新指向生效(blink2.txt),旧指向可回滚(blink2.txt~)。这是版本切换、发布回滚的常用手法。

五、注意事项与常见问题

Q1:硬链接和复制文件有什么区别? 复制产生新 inode、独立的数据副本,之后互不影响;硬链接与原文件同 inode、同一份数据,改动完全同步(见 3.1、3.2 与 cp 篇 3.11 的对照实测)。

Q2:为什么删了原文件,硬链接还能读、软链却坏了? 硬链接是同一 inode 的另一个目录项,只要链接数不为 0 数据就在;软链只是路径文本,目标路径消失即悬空(见 3.4 的对照实测)。

Q3:为什么不能给目录建硬链接? 目录硬链接会形成环,破坏文件系统遍历与 .. 语义,内核直接禁止(见 3.5 报错实测);给目录做"链接"用软链。

Q4:软链可以指向不存在的文件吗? 可以。创建不检查目标存在性(见 3.6),访问时才报 No such file or directory。可用这个特性做"目标稍后出现"的占位,也要小心无意中造出悬空链接。

Q5:ln 提示 File exists 怎么办? 链接名已存在:确认后用 -f 覆盖(3.7),要留旧指向用 -b -f(3.8),人工操作用 -i 逐次确认(3.9)。

Q6:ln -i 回答 n 之后退出码是多少? 0。拒绝替换不视为错误,链接保持原状(见 3.9 的 exit=0 实测)。

Q7:为什么 ln -s 执行完,链接"建到目录里"去了? 链接名本身是指向目录的软链时,ln 默认解引用它、把新链接建进目标目录(见 3.11 的完整复现)。解决:加 -n 把链接名当普通文件,替换指向的标准写法是 ln -sfn。

Q8:ls -l 里权限后的数字 2、3 是什么意思? 是该 inode 的硬链接数:几个目录项指向它。实测中 hard.txt 随新硬链接的建立从 1 涨到 2(3.1)、删除原名字后降回 1(3.4),软链本身不计入目标文件的链接数。

六、参考链接