(zsh) 脚本确定其绝对路径的便携式1方法是什么?
在Linux上我使用类似的东西
mypath=$(readlink -f $0)
...但这不是便携式的。 (例如,readlink
达尔文无法识别该-f
标志,也没有任何等效标志。)(此外,readlink
不可否认,为此使用是一个看起来相当晦涩难懂的黑客行为。)
有什么更便携的方式吗?
1也就是说,可跨 Unix 系列操作系统移植。
答案1
在 zsh 中您可以执行以下操作:
mypath=${0:a}
或者,获取脚本所在的目录:
mydir=${0:a:h}
看关于历史扩展修饰符的 Zsh 文档,如果安装了信息文档,man zshexpn
则在本地可见。info -f zsh -n Modifiers
答案2
对于zsh
,它只是:
mypath=$0:A
或者
mypath=$0:P
在较新的版本中(请参阅手册了解它们有何不同的详细信息)。
现在对于其他 shell,虽然realpath()
和readlink()
是标准函数(后者是系统调用),realpath
但readlink
不是标准命令,尽管某些系统具有其中之一或两者都有不同的行为和功能集。
通常,为了可移植性,您可能需要求助于perl
:
abs_path() {
perl -MCwd -le '
for (@ARGV) {
if ($p = Cwd::abs_path $_) {
print $p;
} else {
warn "abs_path: $_: $!\n";
$ret = 1;
}
}
exit $ret' "$@"
}
readlink -f
与realpath()
(GNU ) 相比,它的行为更像是 GNU,readlink -e
因为只要文件的目录名不存在,它就不会抱怨。
答案3
我已经使用它好几年了:
# The absolute, canonical ( no ".." ) path to this script
canonical=$(cd -P -- "$(dirname -- "$0")" && printf '%s\n' "$(pwd -P)/$(basename -- "$0")")
答案4
假设您确实指的是绝对路径,即来自根目录的路径:
case $0 in
/*) mypath=$0;;
*) mypath=$PWD/$0;;
esac
顺便说一句,这适用于任何 Bourne 风格的 shell。
如果您的意思是所有符号链接都已解析的路径,那就是另一回事了。readlink -f
适用于 Linux(不包括一些精简的 BusyBox 系统)、FreeBSD、NetBSD、OpenBSD 和 Cygwin,但不适用于 OS/X、AIX、HP/UX 或 Solaris。如果有readlink
,则可以循环调用它:
realpath () {
[ -e "$1" ] || return
case $1 in
/*) :;;
*) set "$PWD/$1";;
esac
while [ -L "$1" ]; do
set "${1%/*}" "$(readlink "$1")"
case $2 in
/*) set "$2";;
*) if [ -z "$1" ]; then set "/$2"; else set "$(cd "$1" && pwd -P)/$2"; fi;;
esac
done
case $1 in
*/.|*/..) set "$(cd "$1" && pwd -P)";;
*/./*|*/../*) set "$(cd "${1%/*}" && pwd -P)/${1##*/}"
esac
realpath=$1
}
如果没有readlink
,您可以用 来近似ls -n
,但这仅在ls
不破坏文件名中任何不可打印字符的情况下才有效。
poor_mans_readlink () {
if [ -L "$1" ]; then
set -- "$1" "$(LC_ALL=C command ls -n -- "$2"; echo z)"
set -- "${2%??}"
set -- "${2#*"$1 -> "}"
fi
printf '%s\n' "$1"
}
(额外的z
情况是链接目标以换行符结尾,否则命令替换会耗尽。realpath
顺便说一句,该函数不处理目录名的这种情况。)