Answers:
使用zsh
,只是:
mypath=$0:A
现在对于其他炮弹,但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 readlink -e
)更像GNU ,因为只要目录名不存在文件就不会抱怨。
在zsh中,您可以执行以下操作:
mypath=${0:a}
或者,要获取脚本所在的目录:
mydir=${0:a:h}
来源:zshexpn(1)手册页,“历史扩展”部分,“修饰符”部分(或简称为info -f zsh -n Modifiers
)。
readlink -f
宁愿$0:A
。
我已经使用了几年了:
# The absolute, canonical ( no ".." ) path to this script
canonical=$(cd -P -- "$(dirname -- "$0")" && printf '%s\n' "$(pwd -P)/$(basename -- "$0")")
readlink -f
是脚本本身是一个符号链接。
zsh
,如果我在主目录(/home/ville
)中直接执行此操作或在subshell中执行此操作(如建议的那样),则会打印出/home/ville/zsh
。
这句法应该可以移植到任何Bourne shell的风格解释(与测试bash
,ksh88
,ksh93
,zsh
,mksh
,dash
和busybox sh
):
mypath=$(exec 2>/dev/null;cd -- $(dirname "$0"); unset PWD; /usr/bin/pwd || /bin/pwd || pwd)
echo mypath=$mypath
此版本增加了对传统AT&T Bourne外壳(非POSIX)的兼容性:
mypath=`dirname "$0"`
mypath=`exec 2>/dev/null;(cd -- "$mypath") && cd -- "$mypath"|| cd "$mypath"; unset PWD; /usr/bin/pwd || /bin/pwd || pwd`
echo mypath=$mypath
$PWD
取消设置可能会过大-您可以将其设置为绝对电流,例如cd -P .
。我怀疑这是否可以同时使用-但它应该在您第一次测试的所有程序中都有效。反正为我做。
zsh
,dirname
但很快撤消了他/她的评论……
假设您的意思是绝对路径,即来自根目录的路径:
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
顺便说一句,该函数无法为目录名处理这种情况。)
ls
实现。
touch Stéphane; LC_ALL=C busybox ls Stéphane | cat
→ St??phane
(如果名称使用UTF-8,则latin1给您一个?
)。我想我也已经在较老的商业Unices上看到了。
如果您具有对当前目录(或从中执行Shell脚本的目录)的执行权限,则如果要获得目录的绝对路径,则只需要一个cd
。
如果该
-P
选项有效,则将$PWD
环境变量设置为将会输出的字符串pwd -P
。如果对新目录或该目录的任何父目录没有足够的权限来确定当前的工作目录,$PWD
则未指定环境变量的值。
写入标准输出的路径名不得包含任何引用符号链接类型文件的组件。如果
pwd
实用程序可以将多个路径名写入标准输出,一个以一个/斜杠字符开头,一个或多个以两个/斜杠字符开头,那么它应该以一个/斜杠字符开头写该路径名。路径名不得在前一或两个/斜杠字符之后包含任何不必要的/斜杠字符。
这是因为cd -P
有设置当前工作目录是什么pwd -P
应该,否则打印并认为cd -
具有打印$OLDPWD
,下面的工作:
mkdir ./dir
ln -s ./dir ./ln
cd ./ln ; cd . ; cd -
/home/mikeserv/test/ln
等一下...
cd -P . ; cd . ; cd -
/home/mikeserv/test/dir
当我与cd -
我打印时$OLDPWD
。现在就cd
设置$PWD
为cd -P .
$PWD
绝对路径/
-我不需要任何其他变量。实际上,我什至不需要尾随,.
但在不经过修饰的情况下,有一种特定的行为会重置$PWD
为$HOME
交互式外壳 cd
程序。因此,这只是养成的好习惯。
因此,仅对in的路径进行上述操作就${0%/*}
足以验证$0
的路径,但是$0
不幸的是,在本身是软链接的情况下,您可能无法将目录更改为该路径。
这是一个可以处理的函数:
zpath() { cd -P . || return
_out() { printf "%s$_zdlm\n" "$PWD/${1##*/}"; }
_cd() { cd -P "$1" ; } >/dev/null 2>&1
while [ $# -gt 0 ] && _cd .
do if _cd "$1"
then _out
elif ! [ -L "$1" ] && [ -e "$1" ]
then _cd "${1%/*}"; _out "$1"
elif [ -L "$1" ]
then ( while set -- "${1%?/}"; _cd "${1%/*}"; [ -L "${1##*/}" ]
do set " $1" "$(_cd -; ls -nd -- "$1"; echo /)"
set -- "${2#*"$1" -> }"
done; _out "$1"
); else ( PS4=ERR:\ NO_SUCH_PATH; set -x; : "$1" )
fi; _cd -; shift; done
unset -f _out _cd; unset -v _zdlm
}
它努力在当前shell中尽其所能-不调用子shell-尽管为错误和未指向目录的软链接调用了子shell。它取决于POSIX兼容的外壳程序和POSIX兼容的ls
以及干净的_function()
名称空间。没有后者,它仍然可以正常工作,尽管unset
在这种情况下,它可能会覆盖一些当前的Shell函数。通常,所有这些依赖项在Unix机器上应该是相当可靠的。
无论有无参数调用,首先$PWD
将其重置为规范值-必要时将其中的任何链接解析为目标。调用时不带参数,仅此而已;但与他们一起调用,它将为每个问题解析并规范化路径,否则会打印一条消息,stderr
说明为什么不这样做。
因为它主要在当前shell中运行,所以它应该能够处理任意长度的参数列表。它还会寻找$_zdlm
变量(unset
在通过时也将显示该变量),并在其每个参数的右边立即打印其C转义的值,每个参数之后始终始终带有一个\n
单行字符。
它做了很多目录更改,但是除了将其设置为规范值之外,它不会影响$PWD
,尽管在$OLDPWD
通过时无法以任何方式进行计数。
它试图尽快退出其每个参数。它首先尝试cd
进入$1
。如果可以,则将参数的规范路径打印到stdout
。如果不能,则检查是否$1
存在并且不是软链接。如果为true,则打印。
这样,它可以处理外壳程序有权访问的任何文件类型参数,除非$1
它是不指向目录的符号链接。在这种情况下,它将while
在子Shell中调用loop。
它调用ls
以读取链接。为了可靠地处理任何引用路径,必须首先将当前目录更改为其初始值,因此,在命令替换子Shell中,该函数执行以下操作:
cd -...ls...echo /
它从ls
输出的左侧剥离的内容要少得多,以完全包含链接的名称和字符串->
。虽然我一开始尝试避免使用shift
和$IFS
但事实证明,这是我认为最可靠的方法。这与Gilles的poor_mans_readlink一样,而且做得很好。
它将循环重复此过程,直到从 ls
绝对不是软链接。此时,它会像以前一样规范化该路径,cd
然后进行打印。
用法示例:
zpath \
/tmp/script \ #symlink to $HOME/test/dir/script.sh
ln \ #symlink to ./dir/
ln/nl \ #symlink to ../..
/dev/fd/0 \ #currently a here-document like : dash <<\HD
/dev/fd/1 \ #(zlink) | dash
file \ #regular file
doesntexist \ #doesnt exist
/dev/disk/by-path/pci-0000:00:16.2-usb-0:3:1.0-scsi-0:0:0:0 \
/dev/./././././././null \
. ..
/home/mikeserv/test/dir/script.sh
/home/mikeserv/test/dir/
/home/mikeserv/test/
/tmp/zshtpKRVx (deleted)
/proc/17420/fd/pipe:[1782312]
/home/mikeserv/test/file
ERR: NO_SUCH_PATH: doesntexist
/dev/sdd
/dev/null
/home/mikeserv/test/
/home/mikeserv/
也许...
ls
dir/ file file? folder/ link@ ln@ script* script3@ script4@
zdlm=\\0 zpath * | cat -A
/home/mikeserv/test/dir/^@$
/home/mikeserv/test/file^@$
/home/mikeserv/test/file$
^@$
/home/mikeserv/test/folder/^@$
/home/mikeserv/test/file$ #'link' -> 'file\n'
^@$
/home/mikeserv/test/dir/^@$ #'ln' -> './dir'
/home/mikeserv/test/script^@$
/home/mikeserv/test/dir/script.sh^@$ #'script3' -> './dir/script.sh'
/home/mikeserv/test/dir/script.sh^@$ #'script4' -> '/tmp/script' -> ...
当有可用的python来防止重新定义算法时,同情的一线怎么办?
function readlink { python -c "import os.path; print os.path.realpath('$1')"; }