我想转换/foo/bar/../ foo
是否有bash命令可以做到这一点?
编辑:在我的实际案例中,目录确实存在。
我想转换/foo/bar/../ foo
是否有bash命令可以做到这一点?
编辑:在我的实际案例中,目录确实存在。
当前回答
今天我发现可以使用stat命令来解析路径。
对于"~/Documents"这样的目录:
你可以运行这个:
stat -f %N ~/文档
获取完整路径:
/用户/我/文档
对于符号链接,你可以使用%Y格式选项:
stat -f %Y example_symlink
这可能会返回如下结果:
/usr/local/sbin/example_symlink
格式选项可能在*NIX的其他版本上有所不同,但这些在OSX上对我有用。
其他回答
我做了一个内置函数来处理这个问题,并专注于最高的性能(为了好玩)。它不解析符号链接,因此基本上与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倍,这主要是因为不需要每次都执行新程序。
我知道这是一个古老的问题。我仍在提供另一种选择。最近我遇到了同样的问题,并且发现没有现有的可移植命令来执行此操作。因此,我编写了下面的shell脚本,其中包括一个可以实现此功能的函数。
#! /bin/sh
function normalize {
local rc=0
local ret
if [ $# -gt 0 ] ; then
# invalid
if [ "x`echo $1 | grep -E '^/\.\.'`" != "x" ] ; then
echo $1
return -1
fi
# convert to absolute path
if [ "x`echo $1 | grep -E '^\/'`" == "x" ] ; then
normalize "`pwd`/$1"
return $?
fi
ret=`echo $1 | sed 's;/\.\($\|/\);/;g' | sed 's;/[^/]*[^/.]\+[^/]*/\.\.\($\|/\);/;g'`
else
read line
normalize "$line"
return $?
fi
if [ "x`echo $ret | grep -E '/\.\.?(/|$)'`" != "x" ] ; then
ret=`normalize "$ret"`
rc=$?
fi
echo "$ret"
return $rc
}
https://gist.github.com/bestofsong/8830bdf3e5eb9461d27313c3c282868c
我需要一个同时满足这三点的解决方案:
在普通mac上工作。realpath和readlink -f是插件 解决符号链接 有错误处理
没有一个人的答案同时包含了第一条和第二条。我加了第三条是为了让其他人不用再剃牦牛了。
#!/bin/bash
P="${1?Specify a file path}"
[ -e "$P" ] || { echo "File does not exist: $P"; exit 1; }
while [ -h "$P" ] ; do
ls="$(ls -ld "$P")"
link="$(expr "$ls" : '.*-> \(.*\)$')"
expr "$link" : '/.*' > /dev/null &&
P="$link" ||
P="$(dirname "$P")/$link"
done
echo "$(cd "$(dirname "$P")"; pwd)/$(basename "$P")"
下面是一个简短的测试用例,在路径中有一些扭曲的空格,以充分练习引用
mkdir -p "/tmp/test/ first path "
mkdir -p "/tmp/test/ second path "
echo "hello" > "/tmp/test/ first path / red .txt "
ln -s "/tmp/test/ first path / red .txt " "/tmp/test/ second path / green .txt "
cd "/tmp/test/ second path "
fullpath " green .txt "
cat " green .txt "
一个使用node.js的简单解决方案:
#!/usr/bin/env node
process.stdout.write(require('path').resolve(process.argv[2]));
正如Adam Liss所指出的,realpath并非与每个发行版都捆绑在一起。这很遗憾,因为这是最好的解决方案。提供的源代码很棒,我可能现在就开始使用它。以下是我到目前为止一直在使用的,只是为了完整起见,我在这里分享:
get_abs_path() {
local PARENT_DIR=$(dirname "$1")
cd "$PARENT_DIR"
local ABS_PATH="$(pwd)"/"$(basename "$1")"
cd - >/dev/null
echo "$ABS_PATH"
}
如果您希望它解析符号链接,只需将pwd替换为pwd -P。