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

readlink 命令逐参数实测详解:默认输出软链记录值、-f/-e/-m 三档规范化解析的存在性差异、-n 不换行、-s/-v 错误消息控制、-z NUL 结尾与 realpath 的关系,全部命令与回显在 Ubuntu 22.04(GNU coreutils 8.32)实测通过。

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

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

目标:全面掌握 GNU readlink 的每一个参数——默认输出软链记录值、-f/-e/-m 三档规范化解析的存在性差异、-n 不换行、-s/-v 错误消息控制、-z NUL 结尾,以及与 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")) 即可无视调用方式定位真实位置。

两者在"规范化解析"上同源: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)。

六、参考链接