如果 readlink/realpath 不可用,如何解析 POSIX shell 中的相对路径?

如果 readlink/realpath 不可用,如何解析 POSIX shell 中的相对路径?

如果 utility 和 都不readlink可用realpath(我认为现在主要来自 GNU coreutils?),我可以在 POSIX shell 脚本中使用什么来解析相对路径?

有一个realpath不过,C 函数。所以也许是其他之一公用事业秘密使用它?

答案1

有一个realpath不过,C 函数。所以也许是其他之一公用事业秘密使用它?

从我的头脑中,我不知道这些实用程序中的任何一个realpath在其规范中引用了该函数。

我所知道的是测试对于目录条目,当路径名被解析为现有的目录条目1+x。

窥视中的实施破折号(git.kernel.org), 它用lstat64/stat64

这对于目录来说可能就足够了(测试-d路径名) 到光盘进入它们并返回解析的路径名密码

为了光盘,CD路径应该未设置或为空²,并且操作数不应该是连字符减号“-”

([ -d "$1" ] && [ '-' != "$1" ] && CDPATH= cd -P -- "$1" && pwd -P)

例子:返回目录的绝对路径、规范化路径和物理路径名,如果未解析为目录,则返回 null。

如果排除连字符减号(“-”)作为路径名存在问题,则另一种方法是将操作数设置为“./-”。

1 参见测试; ² 参见cd 步骤 5 说明, cf.cd“-”操作数


正如我所读到的,你已经知道了目录名基本名称,也许还关于如何检查符号链接是否损坏或不存在 Unix&Linux,如果没有,请查看该答案的上下文中是否对此感兴趣”测试用法,例如区分测试 -d 路径名1. 退出状态

答案2

自从发布问题以来,我想出了以下解决方法:

#!/bin/sh
realpath() (
    file=
    path=$1
    [ -d "$path" ] || {
        file=/$(basename -- "$path")
        path=$(dirname -- "$path")
    }
    {
        path=$(cd -- "$path" && pwd)$file
    } || exit $?
    printf %s\\n "/${path#"${path%%[!/]*}"}"
)

警告:显然,到目前为止我还无法测试这么多!始终寻求反馈。如果您看到什么,请发表评论。

另外,我确信其他人以前也有过同样的想法,并且解决得更好,特别是对于边缘情况和经过广泛的测试。如果您了解更多,请发布链接。

上面的 shell 函数realpath是 GNU coreutils 的替代品realpath -s --,请参阅https://www.gnu.org/software/coreutils/manual/html_node/realpath-inplication.html。这意味着没有解析符号链接。 (如果我们只对解析相对路径感兴趣,那么这也是不必要的。)

  • #!/bin/sh
    

    使用 shebang 作为 POSIX shell。

  • realpath() (
    

    在子 shell 中运行该函数,即realpath() (...)而不是realpath() {...},因此我们不会覆盖可能存在的变量filepath。 (POSIX shell 不支持局部变量。)

  • file=
    path=$1
    

    定义变量。最初假设参数给出的路径$1是现有目录。 (这很重要,因为路径的最后一个组成部分可能是...,这是我们要消除的路径的相对部分。)

  • [ -d "$path" ] || {
        file=/$(basename -- "$path")
        path=$(dirname -- "$path")
    }
    

    如果函数参数不是之前假设的现有目录的路径,我们必须调整$path.引用自man realpath

    打印解析后的绝对文件名;除最后一个组件外的所有组件都必须存在

    $file因此,(“最后一个组件”)实际上是现有文件还是不存在的文件或文件夹并不重要- 我们$path现在只是将其删除(并最终再次附加它)。

  • {
        path=$(cd -- "$path" && pwd)$file
    

    这就是“技巧”发生的地方:要解析任何相对路径,我们只需让cd我们解析它们并用于pwd打印我们最终所在的目录。这条新路径被保存在$path.另外,我们不必担心意外地实际更改我们的 cwd,因为2.6.3 命令替换在子 shell 中运行。$file被附加在最后。

  • } || exit $?
    

    {...}此行是围绕上一行变量赋值的代码块的原因。如果用户提供了错误的路径(例如,不仅仅是最后一个组件不存在),cd(从命令替换内部)应该失败并向 stderr 打印一条错误消息(例如“can't cd to ...”) )并退出并返回错误代码 > 0。这也将是命令替换子 shell 的错误代码。要访问此错误代码$?,变量赋值将被包装{...}。因此,如果{...}块失败,我们将退出该函数并传播相同的非零错误代码。

  • printf %s\\n "/${path#"${path%%[!/]*}"}"
    

    由于像///awherea不存在这样的边缘情况,我们最终会得到结果////a。所以2.6.2 参数扩展用于$path在为最终输出添加单个斜杠之前删除 , 中的所有前导斜杠。

以下是我运行的一些测试:

for path in / // /// . .. "$HOME/." "$HOME/.." "*" --help /tmp /notexisting
do
    if [ "$(realpath "$path")" != "$(/usr/bin/realpath -s -- "$path")" ]
    then
        printf %s\\n "$path"
    fi
done

相关内容