给定一个绝对路径或相对路径(在类unix系统中),我希望在解析任何中间符号链接之后确定目标的完整路径。同时还可以解析~用户名符号。
如果目标是一个目录,则可以将chdir()放入该目录,然后调用getcwd(),但我确实希望从shell脚本而不是编写C helper来完成此操作。不幸的是,shell倾向于试图向用户隐藏符号链接的存在(这是OS X上的bash):
$ ls -ld foo bar
drwxr-xr-x 2 greg greg 68 Aug 11 22:36 bar
lrwxr-xr-x 1 greg greg 3 Aug 11 22:36 foo -> bar
$ cd foo
$ pwd
/Users/greg/tmp/foo
$
我想要的是一个函数resolve(),这样当从上面的例子中的tmp目录执行时,resolve("foo") == "/Users/greg/tmp/bar"。
您的路径是目录还是文件?如果它是一个目录,它很简单:
(cd "$DIR"; pwd -P)
然而,如果它可能是一个文件,那么这将不起作用:
DIR=$(cd $(dirname "$FILE"); pwd -P); echo "${DIR}/$(readlink "$FILE")"
因为符号链接可能解析为相对路径或完整路径。
在脚本上,我需要找到真正的路径,这样我就可以引用配置或其他脚本安装在一起,我使用这个:
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
您可以将SOURCE设置为任何文件路径。基本上,只要路径是符号链接,它就解析那个符号链接。诀窍在循环的最后一行。如果解析的符号链接是绝对的,它将使用它作为SOURCE。然而,如果它是相对的,它将在DIR之前为它,通过我首先描述的简单技巧将其解析为一个真实位置。
在这里,我提出了一个我认为是跨平台(至少Linux和macOS)的解决方案,目前对我来说效果很好。
crosspath()
{
local ref="$1"
if [ -x "$(which realpath)" ]; then
path="$(realpath "$ref")"
else
path="$(readlink -f "$ref" 2> /dev/null)"
if [ $? -gt 0 ]; then
if [ -x "$(which readlink)" ]; then
if [ ! -z "$(readlink "$ref")" ]; then
ref="$(readlink "$ref")"
fi
else
echo "realpath and readlink not available. The following may not be the final path." 1>&2
fi
if [ -d "$ref" ]; then
path="$(cd "$ref"; pwd -P)"
else
path="$(cd $(dirname "$ref"); pwd -P)/$(basename "$ref")"
fi
fi
fi
echo "$path"
}
这里有一个macOS(仅?)解决方案。也许更适合最初的问题。
mac_realpath()
{
local ref="$1"
if [[ ! -z "$(readlink "$ref")" ]]; then
ref="$(readlink "$1")"
fi
if [[ -d "$ref" ]]; then
echo "$(cd "$ref"; pwd -P)"
else
echo "$(cd $(dirname "$ref"); pwd -P)/$(basename "$ref")"
fi
}