我想转换/foo/bar/../ foo
是否有bash命令可以做到这一点?
编辑:在我的实际案例中,目录确实存在。
我想转换/foo/bar/../ foo
是否有bash命令可以做到这一点?
编辑:在我的实际案例中,目录确实存在。
当前回答
不完全是一个答案,但可能是一个后续问题(最初的问题不明确):
如果你真的想遵循符号链接,Readlink是很好的。但也有一个用例仅仅是正常化。/和../和//序列,这可以纯语法地完成,而不需要规范化符号链接。Readlink做不到这一点,realpath也不行。
for f in $paths; do (cd $f; pwd); done
适用于现有路径,但不适用于其他路径。
sed脚本似乎是一个不错的选择,除了不能迭代地替换序列(/foo/bar/baz/../..)- > / foo / bar / . .-> /foo)没有使用像Perl这样的东西,这在所有系统上都是不安全的,或者使用一些丑陋的循环来比较sed的输出和它的输入。
FWIW,使用Java (JDK 6+)的一行程序:
jrunscript -e 'for (var i = 0; i < arguments.length; i++) {println(new java.io.File(new java.io.File(arguments[i]).toURI().normalize()))}' $paths
其他回答
realpath试试。以下是全文来源,特此捐赠给公众领域。
// realpath.c: display the absolute path to a file or directory.
// Adam Liss, August, 2007
// This program is provided "as-is" to the public domain, without express or
// implied warranty, for any non-profit use, provided this notice is maintained.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <limits.h>
static char *s_pMyName;
void usage(void);
int main(int argc, char *argv[])
{
char
sPath[PATH_MAX];
s_pMyName = strdup(basename(argv[0]));
if (argc < 2)
usage();
printf("%s\n", realpath(argv[1], sPath));
return 0;
}
void usage(void)
{
fprintf(stderr, "usage: %s PATH\n", s_pMyName);
exit(1);
}
正如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。
我来晚了,但这是我在阅读了一堆这样的帖子后精心设计的解决方案:
resolve_dir() {
(builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"` 2>/dev/null; if [ $? -eq 0 ]; then pwd; fi)
}
这将解析$1的绝对路径,很好地处理~,将符号链接保留在它们所在的路径中,并且不会打乱目录堆栈。它返回完整的路径,如果不存在则不返回。它期望$1是一个目录,如果不是的话可能会失败,但这是一个很容易自己做的检查。
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
}
注意,这个变体也不需要路径存在。
由于所介绍的解决方案都不适合我,所以在文件不存在的情况下,我实现了我的想法。 André Anjos的解决方案有一个问题,路径以../../都解决错了。例如../../a/b/变成了a/b/。
function normalize_rel_path(){
local path=$1
result=""
IFS='/' read -r -a array <<< "$path"
i=0
for (( idx=${#array[@]}-1 ; idx>=0 ; idx-- )) ; do
c="${array[idx]}"
if [ -z "$c" ] || [[ "$c" == "." ]];
then
continue
fi
if [[ "$c" == ".." ]]
then
i=$((i+1))
elif [ "$i" -gt "0" ];
then
i=$((i-1))
else
if [ -z "$result" ];
then
result=$c
else
result=$c/$result
fi
fi
done
while [ "$i" -gt "0" ]; do
i=$((i-1))
result="../"$result
done
unset IFS
echo $result
}