我可以使用外部命令realpath来获取文件的绝对路径:
realpath tmp/toto
Run Code Online (Sandbox Code Playgroud)
回报
/home/john/tmp/toto
Run Code Online (Sandbox Code Playgroud)
我可以使用bash内置函数来获得相同的效果吗?
shellbash没有内置方法来显式获取给定相对路径名的绝对路径。
在zshshell 中,人们可以做
pathname=../../home/kk/.zshenv
print -r -- $pathname:P
Run Code Online (Sandbox Code Playgroud)
并/home/kk/.zshenv返回(请注意,它还会尽可能以类似的方式解析所有符号链接realpath())。
在 中bash,您可以对于指定非目录的路径名并假设您具有对其父目录的搜索访问权限,请使用
pathname=../../home/kk/.zshenv
print -r -- $pathname:P
Run Code Online (Sandbox Code Playgroud)
也就是说,临时(在子 shell 中)cd保存文件的目录,然后使用该值$PWD和路径名的文件名部分构造绝对路径名。需要-P避免cd对..元件进行特殊处理。它具有解析目录本身中的符号链接组件的副作用。但是,如果文件本身是符号链接,则不会解析它(这与zshs:P修饰符不同)。
我们着手OLDPWD解决-这样一个事实:cd -P -- -使用 achdir($OLDPWD)代替chdir("-")(该技巧适用于bash但不适用于所有其他sh实现)。
对于目录路径来说更容易:
$ pathname=../../home/kk/.zshenv
$ ( OLDPWD=- CDPATH= cd -P -- "${pathname%/*}" && printf '%s/%s\n' "$PWD" "${pathname##*/}" )
/home/kk/.zshenv
Run Code Online (Sandbox Code Playgroud)
为了方便起见,这显然可以放入 shell 函数中:
$ pathname=../../home/kk
$ ( OLDPWD=- CDPATH= cd -P -- "$pathname" && pwd )
/home/kk
Run Code Online (Sandbox Code Playgroud)
(请注意,整个函数是在子 shell 中定义的,以避免更改调用 shell 的工作目录。)
测试这个功能:
myrealpath () (
if [[ -d $1 ]]; then
OLDPWD=- CDPATH= cd -P -- "$1" && pwd
else
OLDPWD=- CDPATH= cd -P -- "${1%/*}" && printf '%s/%s\n' "$PWD" "${1##*/}"
fi
)
Run Code Online (Sandbox Code Playgroud)
$ myrealpath ../../home/kk
/home/kk
Run Code Online (Sandbox Code Playgroud)
根据操作系统的不同,使用不带参数的函数将返回当前目录的绝对路径或给出错误。在 的未来版本中bash,这可能是每个系统上的错误,因为cd ''失败将成为 POSIX 要求。
Bash 带有一个realpath可加载的扩展。由于 bash-4.4 应该默认安装\xc2\xb9,所以你应该能够执行以下操作:
BASH_LOADABLES_PATH=${BASH_LOADABLES_PATH:-/usr/local/lib/bash:/usr/lib/bash}\nenable -f realpath realpath\nrealpath ..\nhelp realpath\nRun Code Online (Sandbox Code Playgroud)\n\n一旦启用,每个可加载命令就成为“真正的”内置命令,没有额外的 fork/exec 开销,并且能够操纵 bash 环境(如果提供,例如可加载mypid)。
该-f选项指定可加载扩展二进制文件的路径,如果它不在预期位置或者您的发行版没有正确设置它,您将需要将该变量设置BASH_LOADABLES_PATH为包含扩展的目录(或者-f与可加载的完整路径)。如果设置正确,则不需要上面的第一行。
当编写喜欢但不需要此类扩展的可移植脚本时,我将按照 @Kusalananda\ 的 \xe2\x80\x94 的方式使用包装函数 \xe2\x80\x94 ,以便它可以回退到外部(例如readlink或realpath来自coreutils)。