我想转换/foo/bar/../ foo
是否有bash命令可以做到这一点?
编辑:在我的实际案例中,目录确实存在。
我想转换/foo/bar/../ foo
是否有bash命令可以做到这一点?
编辑:在我的实际案例中,目录确实存在。
当前回答
realpath的问题是它在BSD(或OSX)上不可用。下面是一个简单的配方,摘自Linux Journal上一篇相当古老(2009年)的文章,它是相当可移植的:
function normpath() {
# Remove all /./ sequences.
local path=${1//\/.\//\/}
# Remove dir/.. sequences.
while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
path=${path/${BASH_REMATCH[0]}/}
done
echo $path
}
注意,这个变体也不需要路径存在。
其他回答
基于@Andre的回答,我可能有一个稍微更好的版本,以防有人在一个无循环,完全基于字符串操作的解决方案。对于那些不想取消对任何符号链接的引用(这是使用realpath或readlink -f的缺点)的人来说,它也很有用。
它适用于bash 3.2.25及更高版本。
shopt -s extglob
normalise_path() {
local path="$1"
# get rid of /../ example: /one/../two to /two
path="${path//\/*([!\/])\/\.\./}"
# get rid of /./ and //* example: /one/.///two to /one/two
path="${path//@(\/\.\/|\/+(\/))//}"
# remove the last '/.'
echo "${path%%/.}"
}
$ normalise_path /home/codemedic/../codemedic////.config
/home/codemedic/.config
我做了一个内置函数来处理这个问题,并专注于最高的性能(为了好玩)。它不解析符号链接,因此基本上与realpath -sm相同。
## A bash-only mimic of `realpath -sm`.
## Give it path[s] as argument[s] and it will convert them to clean absolute paths
abspath () {
${*+false} && { >&2 echo $FUNCNAME: missing operand; return 1; };
local c s p IFS='/'; ## path chunk, absolute path, input path, IFS for splitting paths into chunks
local -i r=0; ## return value
for p in "$@"; do
case "$p" in ## Check for leading backslashes, identify relative/absolute path
'') ((r|=1)); continue;;
//[!/]*) >&2 echo "paths =~ ^//[^/]* are impl-defined; not my problem"; ((r|=2)); continue;;
/*) ;;
*) p="$PWD/$p";; ## Prepend the current directory to form an absolute path
esac
s='';
for c in $p; do ## Let IFS split the path at '/'s
case $c in ### NOTE: IFS is '/'; so no quotes needed here
''|.) ;; ## Skip duplicate '/'s and '/./'s
..) s="${s%/*}";; ## Trim the previous addition to the absolute path string
*) s+=/$c;; ### NOTE: No quotes here intentionally. They make no difference, it seems
esac;
done;
echo "${s:-/}"; ## If xpg_echo is set, use `echo -E` or `printf $'%s\n'` instead
done
return $r;
}
注意:这个函数不处理以//开头的路径,因为路径开头的两个双斜杠是实现定义的行为。但是,它可以很好地处理/、///等等。
这个函数似乎正确地处理了所有的边缘情况,但可能还有一些我没有处理的情况。
性能注意:当调用数千个参数时,abspath运行速度比realpath -sm慢10倍左右;当使用单个参数调用abspath时,在我的机器上,abspath的运行速度比realpath -sm快110倍,这主要是因为不需要每次都执行新程序。
使用coreutils包中的readlink实用程序。
MY_PATH=$(readlink -f "$0")
试试我们新的Bash库产品realpath-lib吧,我们已经把它放在GitHub上了,可以免费、不受阻碍地使用。它有完整的文档,是一个很好的学习工具。
它可以解析本地、相对和绝对路径,除了Bash 4+,它没有任何依赖关系;所以它应该适用于任何地方。它是免费的,干净的,简单的和有教育意义的。
你可以:
get_realpath <absolute|relative|symlink|local file path>
这个函数是库的核心:
function get_realpath() {
if [[ -f "$1" ]]
then
# file *must* exist
if cd "$(echo "${1%/*}")" &>/dev/null
then
# file *may* not be local
# exception is ./file.ext
# try 'cd .; cd -;' *works!*
local tmppwd="$PWD"
cd - &>/dev/null
else
# file *must* be local
local tmppwd="$PWD"
fi
else
# file *cannot* exist
return 1 # failure
fi
# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success
}
它还包含get_dirname, get_filename, get_ stemname和validate_path函数。在不同平台上尝试它,并帮助改进它。
如果您想要从路径中删除部分文件名,“dirname”和“basename”是您的朋友,“realpath”也很方便。
dirname /foo/bar/baz
# /foo/bar
basename /foo/bar/baz
# baz
dirname $( dirname /foo/bar/baz )
# /foo
realpath ../foo
# ../foo: No such file or directory
realpath /tmp/../tmp/../tmp
# /tmp
realpath替代品
如果您的shell不支持realpath,您可以尝试
readlink -f /path/here/..
Also
readlink -m /path/there/../../
工作原理与
realpath -s /path/here/../../
在这种情况下,路径不需要存在就可以被规范化。