寻找经过测试的实用程序来获取相对符号链接目标的绝对路径

寻找经过测试的实用程序来获取相对符号链接目标的绝对路径

注意:在这篇文章的原始标题中,我使用了这个词标准在日常意义上的“完善”(因此经过时间考验,与我可以自己推出的快速解决方案形成鲜明对比)。然而,在 Unix 谈话的上下文中,这个词标准具有非常具体(且非常不同)的技术含义。这个词的另一种更正确的解释标准标题中的内容使我的帖子的其余部分不一致。 (感谢 Stéphane Chazelas 指出了这一点。)因此,我修改了标题,替换为标准经测试


作为我在标题中提到的问题的示例,假设我有如下所示的目录结构

/tmp/example
├── a/
│   └── b/
│       └── c/
│           └── d/
│               └── target
└── A/
    └── B/
        ├── C/
        │   └── D/
        │       └── symlink-0 -> ../../symlink-1/b/c/d/target
        └── symlink-1 -> /tmp/example/a

请注意,这/tmp/example/A/B/C/D/symlink-0是一个符号链接,其即时目标是一个相对的小路:

$ readlink /tmp/example/A/B/C/D/symlink-0
../../symlink-1/b/c/d/target

我想得到这个对应的绝对路径即时目标。 IOW,我想执行部分的解决

/tmp/example/A/B/C/D/symlink-0 -> /tmp/example/A/B/symlink-1/b/c/d/target

是否有一个标准的(或者至少是成熟且经过时间考验的)Unix 实用程序来执行此操作?


注意readlink -f解析路径完全(到无符号链接的路径);对于此处讨论的情况,例如:

$ readlink -f /tmp/example/A/B/C/D/symlink-0
/tmp/example/a/b/c/d/target

这意味着readlink -f就是不是我在这里问的问题的答案。


我可以通过向以下zsh函数添加足够的错误检查等来推出自己的函数:

canonicalize () {
    local abspath=$(dirname $1)/$(readlink $1)
    printf -- '%s\n' $abspath:a
}

...但我已经学会(艰难的方式)不要低估稳健地实现这种实用程序的难度,所以如果可能的话,我更愿意使用现有的工具。


FWIW,下面的脚本生成这篇文章的示例:

mkdir -p /tmp/example/a/b/c/d /tmp/example/A/B/C/D
touch /tmp/example/a/b/c/d/target
ln -s /tmp/example/a /tmp/example/A/B/symlink-1
ln -s ../../symlink-1/b/c/d/target /tmp/example/A/B/C/D/symlink-0

答案1

我不知道有任何这样的实用程序。然而,你的方法是有缺陷的(我同意很难做到正确)。

:a修饰符计算绝对路径,无需访问文件系统。特别是,无论是否是符号链接,它都会更改a/b/..为。aa/b

如果您有/a/b -> ../foo符号链接,则/foo仅当/a它本身不是符号链接时才有效。即使您规范化$(dirname $1),它仍然不适用于符号链接,就像/a/b -> x/../../foox本身是符号链接一样。

在你的情况下:

/tmp/example/A/B/C/D/symlink-0 -> ../../symlink-1/b/c/d/target

解析为/tmp/example/A/B/symlink-1/b/c/d/target只是因为符号链接/tmp/example/A/B/C/D也不/tmp/example/A/B/C是。

换句话说,要获得不含..组件的符号链接目标的绝对路径,您可能必须解析多个符号链接,并且无法通过组合文件路径和符号链接目标来单独完成此操作。

如果你想要这样的路径,最简单的(我想说只有合理和/或有用的)方法是获取典范符号链接目标的路径,即所有路径组件既不是...也不是符号链接(可能除了最后一个)。为此,你可以这样做:

zmodload -F zsh/stat b:zstat
canonicalize1() {
  if [ -L "$1" ]; then
    local link
    zstat -A link +link -- "$1" || return
    case $link in
      (/*) ;;
      (*)
        case $1:h in
          (*/) link=$1:h$link;;
          (*) link=$1:h/$link;;
        esac;;
    esac
    printf '%s\n' $link:h:A/$link:t
  else
    printf '%s\n' $1:A
  fi
}

在您的/tmp/example/A/B/C/D/symlink-0符号链接上,仍然给出/tmp/example/a/b/c/d/target,因为符号链接的目标不是符号链接,但如果这不是您想要的,那么我会问:执行以下操作时您想要什么结果:

canonicalize1 /tmp/x/y

/tmp/x/y到 的符号链接在哪里../foo以及/tmp/x到 的符号链接/a/b/c/a, /a/b,/a/b/c本身也是符号链接吗?

相关内容