readlink 命令详解与参数实测(Ubuntu 22.04)
readlink 命令逐参数实测详解:默认输出软链记录值、-f/-e/-m 三档规范化解析的存在性差异、-n 不换行、-s/-v 错误消息控制、-z NUL 结尾与 realpath 的关系,全部命令与回显在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。
readlink 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 GNU readlink 的每一个参数——默认输出软链记录值、
-f/-e/-m三档规范化解析的存在性差异、-n不换行、-s/-v错误消息控制、-zNUL 结尾,以及与 realpath 的关系,全部命令与回显已在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- readlink 版本:GNU coreutils 8.32(
readlink --version实测) - readlink 属于 coreutils 基础工具集,Ubuntu 22.04 默认预装,无需额外安装
- 演示目录:
/root/demo/readlink/ - 测试数据:
real/sub/file.txt(真实文件)、绝对软链abs.lnk、相对软链rel.lnk、悬空软链dangling.lnk(目标不存在)、自环软链loop(指向自身)
二、命令概览
2.1 语法与测试数据准备
readlink 的基本语法为 readlink [OPTION]... FILE...,职责有两层:默认打印软链的记录值(创建链接时写的目标字符串,原样输出、不做任何解析);配合 -f/-e/-m 则把路径规范化为绝对路径——逐段跟随软链、消解 .. 与 .,这是它最常用的进阶形态。
💡 技术拓展:"看一个链接到底指向哪"有多种查看方式,横向对比——
工具 原理/职责 适用场景 readlink 原样输出软链记录值, -f/-e/-m全链规范化脚本里拿链接的最终指向 realpath 规范化为绝对路径并支持存在性校验 需要校验路径真实存在时 ls -l 首列 l标识 +链接名 -> 目标箭头显示人眼浏览目录时顺带看链接 stat -c %N %N直接给出解引用链看多级链接的逐层指向 本文主角是 readlink:它是唯一"只打印记录值、不做解析"的专用软链工具,配合
-f又能一步到位拿到规范化绝对路径。
先准备一套演示数据,覆盖软链的四种典型形态:
mkdir -p /root/demo/readlink/real/sub
printf 'demo\n' > /root/demo/readlink/real/sub/file.txt
ln -s /root/demo/readlink/real/sub/file.txt /root/demo/readlink/abs.lnk
ln -s real/sub/file.txt /root/demo/readlink/rel.lnk
ln -s /root/demo/readlink/nowhere.txt /root/demo/readlink/dangling.lnk
ln -s /root/demo/readlink/loop /root/demo/readlink/loop
验证数据就绪:
$ ls -l /root/demo/readlink
total 4
lrwxrwxrwx 1 root root 37 Oct 15 06:11 abs.lnk -> /root/demo/readlink/real/sub/file.txt
lrwxrwxrwx 1 root root 31 Oct 15 06:11 dangling.lnk -> /root/demo/readlink/nowhere.txt
lrwxrwxrwx 1 root root 24 Oct 15 06:11 loop -> /root/demo/readlink/loop
drwxr-xr-x 3 root root 4096 Oct 15 06:11 real
lrwxrwxrwx 1 root root 17 Oct 15 06:11 rel.lnk -> real/sub/file.txt
四个软链分别为:绝对目标、相对目标、悬空(目标不存在)、自环(指向自身)。
2.2 --help 全量回显
readlink --help 输出全部参数清单(实测原样保留,可作为参数速查):
Usage: readlink [OPTION]... FILE...
Print value of a symbolic link or canonical file name
-f, --canonicalize canonicalize by following every symlink in
every component of the given name recursively;
all but the last component must exist
-e, --canonicalize-existing canonicalize by following every symlink in
every component of the given name recursively,
all components must exist
-m, --canonicalize-missing canonicalize by following every symlink in
every component of the given name recursively,
without requirements on components existence
-n, --no-newline do not output the trailing delimiter
-q, --quiet
-s, --silent suppress most error messages (on by default)
-v, --verbose report error messages
-z, --zero end each output line with NUL, not newline
--help display this help and exit
--version output version information and exit
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/readlink>
or available locally via: info '(coreutils) readlink invocation'
💡 说明:三个规范化参数只差一个约束——
-f除末段外必须存在、-e全部必须存在、-m不检查存在性。注意帮助里-s标注 "(on by default)":coreutils 8.32 的 readlink 错误消息默认已抑制,-v才打开报错(见 3.9 实测)。
2.3 版本确认
$ readlink --version | head -1
readlink (GNU coreutils) 8.32
三、参数逐项实测
3.1 默认行为:原样输出软链的记录值
不带参数,readlink 打印软链创建时写入的目标字符串——原样输出,不解析、不转绝对路径:
$ readlink /root/demo/readlink/abs.lnk
/root/demo/readlink/real/sub/file.txt
$ readlink /root/demo/readlink/rel.lnk
real/sub/file.txt
$ readlink /root/demo/readlink/dangling.lnk
/root/demo/readlink/nowhere.txt
绝对软链输出绝对目标;相对软链 rel.lnk 输出的就是记录值 real/sub/file.txt 这个相对串本身(相对于链接所在目录);悬空软链照样输出记录值——默认模式完全不检查目标是否存在。
3.2 对普通文件与不存在路径:无输出,退出码 1
readlink 的系统调用 readlink() 只对软链有效,对普通文件返回 EINVAL;路径不存在同样失败。两种情况都是无输出、退出码 1:
$ readlink /root/demo/readlink/real/sub/file.txt; echo exit=$?
exit=1
$ readlink /root/demo/readlink/nosuch; echo exit=$?
exit=1
普通文件与不存在路径从 stdout 上无法区分(都是空),区别只存在于 -v 的报错文本(见 3.9)。
3.3 -f:规范化解析(末段允许不存在)
-f(--canonicalize)逐段跟随软链、消解 . 与 ..,输出最终的绝对路径。约束:除最后一个成分外,其余成分必须存在;末段可以不存在:
$ readlink -f /root/demo/readlink/rel.lnk
/root/demo/readlink/real/sub/file.txt
$ readlink -f /root/demo/readlink/nosuch.txt; echo exit=$?
/root/demo/readlink/nosuch.txt
exit=0
$ readlink -f /root/../root/demo/readlink/rel.lnk
/root/demo/readlink/real/sub/file.txt
第一个示例把相对软链解析成完整绝对路径;第二个示例对不存在的文件也能返回规范化路径(/root、/root/demo、/root/demo/readlink 都存在,只有末段 nosuch.txt 不存在,符合 -f 约束),退出码 0;第三个示例演示 .. 被消解。对普通文件与目录同样适用(无软链可跟就直接规范化):
$ readlink -f /root/demo/readlink/real/sub/file.txt
/root/demo/readlink/real/sub/file.txt
$ readlink -f /root/demo/readlink/real/sub
/root/demo/readlink/real/sub
💡 说明:
-f是 readlink 使用率最高的形态,与默认行为"一字之差、职责全变"——默认只透传记录值(不访问文件系统、不校验存在性),-f则逐段解析并要求中间段真实存在。-e(全链必须存在)与-m(全不检查)是它的两个松紧旋钮,对照实测见 3.5/3.6。
3.4 -f 的存在性约束:中间段缺失即失败
末段可以不存在,但中间目录缺失时 -f 失败(无输出、退出码 1)——因为中间段是软链就要跟,跟不了就无从规范化:
$ readlink -f /root/demo/readlink/ghostdir/file.txt; echo exit=$?
exit=1
ghostdir(父目录)不存在,违反"除末段外全部存在"的约束,命令失败。这是 -f 与 -m 的分水岭。
3.5 -e:所有成分必须存在
-e(--canonicalize-existing)比 -f 更严:包括末段在内所有成分都必须存在,否则失败:
$ readlink -e /root/demo/readlink/rel.lnk; echo exit=$?
/root/demo/readlink/real/sub/file.txt
exit=0
$ readlink -e /root/demo/readlink/nosuch.txt; echo exit=$?
exit=1
$ readlink -e /root/demo/readlink/ghostdir/file.txt; echo exit=$?
exit=1
第一个示例全段存在,成功输出规范化路径;第二个示例末段不存在即失败(对比 3.3 中 -f 对同一路径返回 exit=0);第三个示例中间段缺失同样失败。需要"结果路径一定真实存在"保证时用 -e,例如写入文件前的安全检查。
3.6 -m:不检查存在性
-m(--canonicalize-missing)最宽松:只做软链解析与规范化,任何成分都可以不存在:
$ readlink -m /root/demo/readlink/ghostdir/file.txt; echo exit=$?
/root/demo/readlink/ghostdir/file.txt
exit=0
同样的路径,-f 失败(3.4)、-e 失败(3.5)、-m 成功。三个参数的存在性约束对比:
| 参数 | 存在性约束 | ghostdir/file.txt 实测结果 |
|---|---|---|
-f | 除末段外必须存在 | 失败(exit=1,无输出) |
-e | 全部成分必须存在 | 失败(exit=1,无输出) |
-m | 不检查存在性 | 成功(exit=0,输出规范化路径) |
选型一句话:常规取真实路径用 -f(对"目标文件尚未创建"宽容);结果必须可访问用 -e;纯粹做字符串规范化(如拼计划中的路径)用 -m。
3.7 -n:不输出末尾换行
-n(--no-newline)去掉结果末尾的换行符,便于把输出直接拼进字符串:
$ readlink -n /root/demo/readlink/abs.lnk | xxd
00000000: 2f72 6f6f 742f 6465 6d6f 2f72 6561 646c /root/demo/readl
00000010: 696e 6b2f 7265 616c 2f73 7562 2f66 696c ink/real/sub/fil
00000020: 652e 7478 74 e.txt
$ readlink /root/demo/readlink/abs.lnk | xxd
00000000: 2f72 6f6f 742f 6465 6d6f 2f72 6561 646c /root/demo/readl
00000010: 696e 6b2f 7265 616c 2f73 7562 2f66 696c ink/real/sub/fil
00000020: 652e 7478 740a e.txt.
xxd 对照:默认输出以 0a(换行)结尾,-n 输出无结尾符。-n 与 -f 组合也有效:
$ readlink -f -n /root/demo/readlink/rel.lnk | xxd
00000000: 2f72 6f6f 742f 6465 6d6f 2f72 6561 646c /root/demo/readl
00000010: 696e 6b2f 7265 616c 2f73 7562 2f66 696c ink/real/sub/fil
00000020: 652e 7478 74 e.txt
3.8 -z:NUL 结尾输出
-z(--zero)用 NUL 字节替代换行作为分隔符,多结果时逐个以 \0 相接:
$ readlink -z /root/demo/readlink/abs.lnk /root/demo/readlink/rel.lnk | xxd
00000000: 2f72 6f6f 742f 6465 6d6f 2f72 6561 646c /root/demo/readl
00000010: 696e 6b2f 7265 616c 2f73 7562 2f66 696c ink/real/sub/fil
00000020: 652e 7478 7400 7265 616c 2f73 7562 2f66 e.txt.real/sub/f
00000030: 696c 652e 7478 7400 ile.txt.
两个结果 .../file.txt\0 与 real/sub/file.txt\0 以 NUL 相接。消费端用 xargs -0、read -d '' 读取,可安全处理含换行符的文件名。
3.9 -s/-q 与 -v:错误消息控制
coreutils 8.32 中 readlink 的错误消息默认已抑制(帮助原文 "-s ... (on by default)"):失败时无输出、退出码 1,stderr 上也没有任何信息:
$ readlink /root/demo/readlink/nosuch 2>&1; echo exit=$?
exit=1
-v(--verbose)打开错误报告,可区分两类失败:
$ readlink -v /root/demo/readlink/nosuch; echo exit=$?
readlink: /root/demo/readlink/nosuch: No such file or directory
exit=1
$ readlink -v /root/demo/readlink/real/sub/file.txt; echo exit=$?
readlink: /root/demo/readlink/real/sub/file.txt: Invalid argument
exit=1
第一条是路径不存在(No such file or directory),第二条是普通文件(Invalid argument,来自 readlink 系统调用的 EINVAL)——默认静默时两者在输出上无法区分,排障时用 -v。-s/-q 在此版本实际效果与默认一致,主要用于显式覆盖 -v 或在脚本中表达"只要退出码"的意图:
$ readlink -s /root/demo/readlink/abs.lnk; echo exit=$?
/root/demo/readlink/real/sub/file.txt
exit=0
$ readlink -s /root/demo/readlink/nosuch; echo exit=$?
exit=1
注意 -s 不抑制正常输出(第一条照常打印了目标),它只管错误消息——"只看退出码"应配合重定向 >/dev/null 使用。
3.10 多参数逐行输出
readlink 支持多个 FILE,逐行输出;-f 可同时规范化多个路径:
$ readlink -f /root/demo/readlink/rel.lnk /root/demo/readlink/abs.lnk
/root/demo/readlink/real/sub/file.txt
/root/demo/readlink/real/sub/file.txt
相对软链与绝对软链殊途同归,都解析到同一个真实文件。
3.11 软链循环的处理
自环软链 loop 指向自身,默认模式只输出记录值(不解析所以不递归):
$ readlink /root/demo/readlink/loop; echo exit=$?
/root/demo/readlink/loop
exit=0
$ readlink -f /root/demo/readlink/loop; echo exit=$?
exit=1
-f 需要逐段跟随软链,遇到自环 ELOOP 失败,退出码 1。内核对软链解析深度有上限(默认 40 层),readlink 靠退出码表达失败,不会无限循环。
四、实战示例
4.1 取软链的真实绝对路径
$ readlink -f /root/demo/readlink/rel.lnk
/root/demo/readlink/real/sub/file.txt
运维中最常见的用法:确认 /usr/bin/python3 这类"多级软链"最终指向哪个真实文件——readlink -f $(which python3)。
4.2 脚本定位自身真实目录(SCRIPT_DIR 技巧)
脚本可能被软链调用,用 readlink -f 拿到脚本真实路径再取目录:
$ ln -sf /root/demo/readlink/real/sub /root/demo/readlink/bin && readlink -f /root/demo/readlink/bin
/root/demo/readlink/real/sub
/root/demo/readlink/bin 是指向目录的软链,-f 一步还原真实目录。脚本里配合 dirname/basename:SCRIPT_DIR=$(dirname $(readlink -f "$0")) 即可无视调用方式定位真实位置。
4.3 readlink -f 与 realpath 的关系
两者在"规范化解析"上同源:readlink -f 路径 与 realpath 路径(不带参数)的默认行为一致——都是逐段跟随软链、消解 ..,且除末段外其余成分必须存在,对不存在的末段返回计划中的绝对路径:
$ realpath /root/demo/readlink/nosuch.txt; echo exit=$?
/root/demo/readlink/nosuch.txt
exit=0
$ realpath /root/demo/readlink/rel.lnk
/root/demo/readlink/real/sub/file.txt
$ readlink -f /root/demo/readlink/rel.lnk
/root/demo/readlink/real/sub/file.txt
对比结论一句话:readlink 的本职是"读软链记录值",-f 是借用其解析能力做规范化;realpath 则是专职的路径规范化工具(同样提供 -e/-m,另有相对路径基准 -L 等能力),仅需规范化时优先 realpath,需要"区分软链与普通路径"语义时用 readlink。
五、注意事项与常见问题
Q1:readlink 普通文件 为什么什么都不输出?
readlink 系统调用只对软链有效,普通文件返回 EINVAL,命令静默失败退出码 1;要看失败原因加 -v(见 3.2、3.9)。
Q2:readlink 相对软链 输出的相对路径怎么用?
它只是记录值,相对于链接所在目录而非当前目录。要可用路径请用 readlink -f(见 3.1、3.3)。
Q3:-f 对不存在的路径为什么不报错?
-f 的约束是"除末段外必须存在",末段缺失时仍输出规范化路径且退出码 0;中间目录缺失才失败(见 3.3、3.4)。
Q4:怎么保证拿到的路径一定真实存在?
用 -e:所有成分必须存在,否则失败。写入前校验、删除前校验都用它(见 3.5)。
Q5:-f、-e、-m 到底选哪个?
看存在性要求:宽容(目标可未建)-f,严格(必须可访问)-e,纯字符串规范化 -m(见 3.6 对比表)。
Q6:-s 能让命令"零输出"吗?
不能。-s 只抑制错误消息(且此版本默认已抑制),正常输出照常;"只看退出码"要加 >/dev/null(见 3.9)。
Q7:软链循环会死循环吗?
不会。-f 跟随软链遇 ELOOP 直接失败退出码 1;默认模式不解析软链更是无感(见 3.11)。
Q8:readlink -f 和 realpath 怎么选? 默认规范化两者输出一致;realpath 是专职规范化工具、能力更全,readlink 保留"读软链记录值"的原始语义(见 4.3)。
六、参考链接
- GNU coreutils 官方手册(readlink 章节):https://www.gnu.org/software/coreutils/manual/html_node/readlink-invocation.html
- 在线文档:https://www.gnu.org/software/coreutils/readlink
- 本机帮助:
readlink --help、man readlink(Ubuntu 22.04 内置) - 关联手册页:
man symlink、man path_resolution(软链解析机制)