ln 命令详解与参数实测(Ubuntu 22.04)
GNU ln 参数实测:硬链接与符号链接的同 inode/悬空对照、-s/-f/-b/-i/-v/-n 逐项行为与链接建进目录的经典坑,Ubuntu 22.04 实测。
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),软链本身不计入目标文件的链接数。
六、参考链接
- GNU coreutils 官方手册(ln 章节):https://www.gnu.org/software/coreutils/manual/html_node/ln-invocation.html
- 在线文档:https://www.gnu.org/software/coreutils/ln
- 本机帮助:
ln --help、man ln(Ubuntu 22.04 内置) - 关联手册页:
man symlink(软链解析机制)、man 2 link/man 2 symlink(两类链接的系统调用)