硬链接是给同一份数据再起一个名字,软链接是存了一条目标路径。ln 的默认行为、两条硬限制、相对路径与换目录链接的坑,均经 WSL 实测。
一、先分清:一个加名字,一个存路径
ln 不带选项建的是硬链接,加 -s 才建符号链接(软链接),这是 GNU 手册的原话;手册把硬链接解释成”已有文件的另一个名字”,和原名不可区分、共享同一个 inode [1]。软链接存的则是一段任意字符串,字符串访问时解析不到文件就成了悬空链接,而且创建悬空的软链接不受任何限制 [1]。差别落到操作上就是这样:
| 属性 | 硬链接 | 软链接 | | — | — | — | | 创建命令 | ln 目标 链接名 | ln -s 目标 链接名 | | 本质 | 同一 inode 的又一个名字 | 存着目标路径的特殊文件 | | 删掉原文件后 | 内容照常可读 | 悬空,立刻打不开 | | 跨文件系统 | 不允许 | 允许 | | 指向目录 | 不允许 | 允许 |
二、建硬链接:用 stat 验证
mkdir -p /tmp/lltest && cd /tmp/lltest
echo data1 > orig.txt
ln orig.txt hard.txt
stat -c "%n inode=%i links=%h" orig.txt hard.txt
echo appended >> hard.txt && cat orig.txt
手册的用法定义:给两个文件名时,ln 把第二个建成指向第一个的链接;建硬链接时目标必须已经存在 [1][2]。实测两个名字的 inode 号相同、links=2;往 hard.txt 追加后 cat orig.txt 两行内容同时出现——两个名字完全对等,没有”原文件”一说。stat 的 %h 是剩余名字数,每删一个硬链接名减一(实测)。
三、建软链接:相对路径按链接自己所在的目录解析
cd /tmp/lltest
ln -s /tmp/lltest/orig.txt abs.txt # 目标写绝对路径
ln -s orig.txt rel.txt # 目标写相对路径
ln -sr /tmp/lltest/note.txt sublink.txt # -r 配合 -s,自动算相对路径
ls -l abs.txt rel.txt sublink.txt
rm orig.txt
cat hard.txt # 硬链接不受影响
cat rel.txt # 悬空:No such file or directory,退出码 1
readlink rel.txt # 仍打出记录的目标路径 orig.txt
两个容易错的点。其一,目标写成相对路径时,解析起点是链接文件所在的目录,不是执行 ln 时所在的目录;把链接挪个位置就可能断,生成时可以用 -r,手册页的说法是”with -s, create links relative to link location” [2]。其二,删掉目标后 cat 报 No such file or directory,但 readlink 照常打出链接里存的路径,排错时先 readlink 再看目标在不在。
四、硬链接的两条硬限制
mkdir d
ln d dhard # hard link not allowed for directory
ln -d d dhard2 # 即使 root:Operation not permitted
ln /tmp/lltest/note.txt /dev/shm/cross.txt # Invalid cross-device link
ln -s /tmp/lltest/note.txt /dev/shm/ok.txt # 软链接跨文件系统:成功
手册口径很明确:硬链接不能跨文件系统边界,且多数系统禁止对目录建硬链接 [1]。实测从 ext4 到 /dev/shm(tmpfs)报 Invalid cross-device link、退出码 1;对目录建硬链接,普通用户报 hard link not allowed for directory,加 -d 以 root 执行也报 Operation not permitted——-d 只是”允许特权用户尝试”,手册页自己注明”即使超级用户也多半会因系统限制失败” [3]。软链接两条都不受限,这是它被广泛用于版本目录切换的原因。
五、换目录软链接的指向:用 ln -sfn
mkdir v1 v2
ln -s /tmp/lltest/v1 dlink
ln -s /tmp/lltest/v2 dlink # 坑:退出码 0,新链接却落在了 v1 里面
ls -l v1 # 多出一个指向 v2 的 v2
ln -sfn /tmp/lltest/v2 dlink # 正确姿势:替换 dlink 本身
ls -l dlink
当链接名本身是指向目录的软链接时,ln 默认会把它当目录钻进去,在其指向的目录里新建链接,命令退出码还是 0,属于不报错的那类坑(实测)。-n 的作用就是让这种链接名按普通文件对待,配合 -f 移除旧链接再建新的 [2],所以换指向的标准写法是 -sfn 三连。
六、删除链接、撤销与报错速查
rm 链接名只删链接本身:实测 rm dlink 之后 v1、v2 目录都完好;删掉一个硬链接名,另一个的 links 从 2 变 1、内容完整。改指向就用 ln -sfn 重建,这就是创建类操作的撤销方式。
| 现象 | 原因 | 处理 | | — | — | — | | ln 报 hard link not allowed for directory | 硬链接不能指向目录 | 改用 ln -s | | ln 报 Invalid cross-device link | 硬链接不能跨文件系统 | 用 ln -s,或留在同一文件系统内 | | 加 -d 仍报 Operation not permitted | 内核层拒绝目录硬链接 | 放弃该做法 | | ls 看得到链接、cat 却报 No such file | 目标已删,链接悬空 | readlink 找目标,ln -sfn 指到新目标 | | ln -s 退出码 0 但指向没变 | 新链接建进了目标目录 | 删掉误建文件,用 ln -sfn 重做 |
## 附:实测记录
参考资料
- [1] GNU Coreutils manual: ln invocation(coreutils 手册)
- [2] ln(1) – Linux manual page (man7.org)(ln(1) 手册页)
- [3] ln(1) – coreutils – Debian bookworm man page(Debian ln(1))
文中命令已在 WSL Ubuntu 实机执行验证。