答案1
从我的头脑中,我不知道这些实用程序中的任何一个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() {...}
,因此我们不会覆盖可能存在的变量file
和path
。 (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%%[!/]*}"}"
由于像
//
或/a
wherea
不存在这样的边缘情况,我们最终会得到结果//
或//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