我正在寻找一个命令来返回文件的绝对路径,而不解析符号链接。一般来说,realpath
这方面做得很好。
$ mkdir /tmp/test; cd /tmp/test
$ mkdir foo
$ ln -s foo bar
$ realpath -se bar # good, this does not resolve the symlink
/tmp/test/bar
它也适用于符号链接目录内的文件。
$ touch foo/file
$ realpath -se bar/file # good, this does not resolve the symlink
/tmp/test/bar/file
然而,当现任董事是符号链接目录
$ cd bar
$ pwd
/tmp/test/bar
$ realpath -se file # this fails, returning the target
/tmp/test/foo/file
$ realpath -se . # this also fails, returning the target
/tmp/test/foo
$ realpath -se /tmp/test/bar/file # yet this works
/tmp/test/bar/file
$ realpath -se /tmp/test/bar # and this works
/tmp/test/bar
为什么会有realpath
这样的行为? (这是一个错误吗?)有没有办法realpath
永远不解析符号链接,或者我应该使用其他方法吗?
答案1
进程的当前工作目录 (CWD) 在操作系统级别从前一个进程继承,或者可以使用chdir(2)
.操作系统(这里我的意思是“内核”)当然将始终解析任何符号链接以确定最终结果,该结果必须是目录,而不是符号链接(到目录)。例如,当有太多符号链接需要解析时,前面的系统调用 ( chdir(2)
) 可能会返回错误。ELOOP
因此,从操作系统的角度来看,不可能存在 CWD 不是任何进程的目录:操作系统将始终将其解析为真实路径,而在任何地方都没有任何符号链接。
一旦 shell 完成cd /tmp/test/bar
,CWD 路径就会被操作系统解析为/tmp/test/foo
.例如,在 Linux 系统上,ls -l /proc/$$/cwd
将显示内核所看到的已解析路径的链接:/tmp/test/foo
。
bar
shell 仍然显示在提示符中的事实是因为它记住了光盘之前完成的命令。该行为可能取决于 shell 类型。我假设这里有 bash。因此,它是内置的pwd
(但不是外部/bin/pwd
命令),$PWD
变量及其使用$PS1
将向用户“撒谎”有关当前目录的信息。
任何进程,例如realpath
或/bin/pwd
从 shell 运行的进程当然都会继承实际的CWD,即/tmp/test/foo
.所以这不是 中的错误realpath
,它永远不会有关于 的具体信息bar
。
正如 Kusalananda 所建议的,一种可能的尴尬方法是以某种方式重用该变量,并仅在其参数不是绝对的情况下$PWD
将其添加到 的参数之前。realpath
这是一个例子。我不确定是否有办法滥用它。例如,虽然下面的函数可以应付,$PWD
但变量本身在 bash 4.4.12 (Debian 9) 中表现不佳,但如果路径中有换行符,则在 bash 5.0.3 (Debian 10) 中工作正常。当某处有换行符时,为了有用,-z
还应该添加一个选项realpath
,但我不会在这个简单的示例中重新实现选项的整个解析。
myrealpathnofollowsym () {
for p in "$@"; do
if ! printf '%s' "$p" | grep -q -- '^/'; then
realpath -se "$PWD/$p"
else
realpath -se "$p"
fi
done
}