例如,在脚本内部,您可以按以下方式使用它来获取运行脚本真正的原始目录,其中解析了符号链接:
trueScriptDir=$(dirname -- "$(rreadlink "$0")")
rreadlink
脚本/函数定义:
此代码是在感激之情下改编自这个回答。
我还创建了一个基于 bash
的独立实用程序版本此处,您可以通过以下方式安装:
npm install rreadlink -g
,如果您已安装 Node.js。
#!/bin/sh
rreadlink() (
target=$1 fname= targetDir= CDPATH=
{ \unalias command; \unset -f command; } >/dev/null 2>&1
[ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on
while :; do
[ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
command cd "$(command dirname -- "$target")"
fname=$(command basename -- "$target")
[ "$fname" = '/' ] && fname=''
if [ -L "$fname" ]; then
target=$(command ls -l "$fname")
target=${target#* -> }
continue
fi
break
done
targetDir=$(command pwd -P)
if [ "$fname" = '.' ]; then
command printf '%s\n' "${targetDir%/}"
elif [ "$fname" = '..' ]; then
command printf '%s\n' "$(command dirname -- "${targetDir}")"
else
command printf '%s\n' "${targetDir%/}/$fname"
fi
)
rreadlink "$@"
关于安全的话题:
jarno在评论中提到了一个函数,确保内置的command
没有被同名的别名或者shell函数所覆盖,他问道:
如果unalias
或者unset
以及[
被设置为别名或者shell函数会怎样呢?
rreadlink
确保command
具有其原始含义的动机是将其用于绕过交互式shell中经常用于隐藏标准命令的(良性)方便别名和函数,例如重新定义ls
以包括喜欢的选项等。
我认为可以肯定地说,除非你处理不受信任的恶意环境,否则不必担心unalias
或者unset
- 或者,就此事而言,while
,do
,... 被重新定义。
函数必须依赖于某些东西才能保持其原始的含义和行为 - 这是无法避免的。
类似 POSIX 的 shell 允许重新定义内置命令甚至语言关键字,这本质上是一种安全风险(编写偏执代码通常很困难)。
针对您的具体问题:
该函数依赖于 unalias
和 unset
保持其原始含义。如果将它们重新定义为更改其行为的 shell 函数,那么就会出现问题;将其重新定义为 别名 不一定是一个问题,因为在命令名称中使用 引号(例如,\unalias
)可以绕过别名。
然而,引用对于 shell 的关键词 (while
、for
、if
、do
等)是不可行的选项。尽管 shell 关键词比 shell 的函数更优先,但在 bash
和 zsh
中,别名具有最高优先级。因此,为了防止 shell 关键词的重新定义,必须使用它们的名称运行 unalias
命令(在非交互式的 bash
shell(例如脚本)中,默认情况下不扩展别名 - 只有在显式调用 shopt -s expand_aliases
时才会扩展别名)。
为了确保作为内置命令的 unalias
具有其原始含义,必须首先使用 \unset
对其进行取消设置,这需要 unset
具有其原始含义:
unset
是一个 shell 内置命令,因此为了确保它被调用为内置命令,您必须确保它本身没有被重新定义为函数。虽然您可以通过引用来绕过别名形式,但无法绕过 shell 函数形式 - 这是个死结。
因此,除非您可以依赖于 unset
具有其原始含义,否则从我所知,没有保证的方法来防御所有恶意重新定义。
bash
,ksh
和zsh
中可以定义while
作为一个函数(但不是dash
),但只能使用function <name>
语法:function while { echo foo; }
可行(while() { echo foo; }
不行)。但是,这不会遮蔽while
关键字,因为关键字的优先级高于函数(唯一调用此函数的方法是\while
)。在bash
和zsh
中,别名比关键字具有更高的优先级,因此关键字的_别名_重新定义确实会遮蔽它们(但在bash
中默认仅在_交互式_ shell 中,除非明确调用shopt -s expand_aliases
)。 - mklement0