我不愿意再使用另一种实现,但是我需要a)一个可移植的纯shell实现,以及b)单元测试的覆盖范围,因为类似这样的边缘情况的数量是不平凡的。
有关测试和完整代码,请参见我在Github上的项目。以下是实现的概要:
正如Keith Smith指出的那样,readlink -f
它做了两件事:1)递归地解决符号链接,以及2)对结果进行规范化,因此:
realpath() {
canonicalize_path "$(resolve_symlinks "$1")"
}
首先,symlink解析器实现:
resolve_symlinks() {
local dir_context path
path=$(readlink -- "$1")
if [ $? -eq 0 ]; then
dir_context=$(dirname -- "$1")
resolve_symlinks "$(_prepend_path_if_relative "$dir_context" "$path")"
else
printf '%s\n' "$1"
fi
}
_prepend_path_if_relative() {
case "$2" in
/* ) printf '%s\n' "$2" ;;
* ) printf '%s\n' "$1/$2" ;;
esac
}
请注意,这是完整实现的稍微简化的版本。完整的实现对符号链接周期进行了少量检查,并对输出进行了一些调整。
最后,用于规范化路径的函数:
canonicalize_path() {
if [ -d "$1" ]; then
_canonicalize_dir_path "$1"
else
_canonicalize_file_path "$1"
fi
}
_canonicalize_dir_path() {
(cd "$1" 2>/dev/null && pwd -P)
}
_canonicalize_file_path() {
local dir file
dir=$(dirname -- "$1")
file=$(basename -- "$1")
(cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file")
}
就是这样,或多或少。粘贴到脚本中很简单,但是却很棘手,以至于您会疯狂地依赖任何没有针对用例的单元测试的代码。
readlink
可以是内置命令或外部命令。