我想转换/foo/bar/../ foo
是否有bash命令可以做到这一点?
编辑:在我的实际案例中,目录确实存在。
我想转换/foo/bar/../ foo
是否有bash命令可以做到这一点?
编辑:在我的实际案例中,目录确实存在。
当前回答
我需要一个同时满足这三点的解决方案:
在普通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 "
其他回答
我知道这是一个古老的问题。我仍在提供另一种选择。最近我遇到了同样的问题,并且发现没有现有的可移植命令来执行此操作。因此,我编写了下面的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
一种可移植且可靠的解决方案是使用python,几乎所有地方都预装了python(包括Darwin)。你有两个选择:
Abspath返回一个绝对路径,但不解析符号链接: import os,sys;打印(os.path.abspath (sys.argv[1]))“路径/ /文件 Realpath返回一个绝对路径并解析符号链接,生成一个规范路径: import os,sys;打印(os.path.realpath (sys.argv[1]))“路径/ /文件
在每种情况下,路径/to/file可以是相对路径,也可以是绝对路径。
基于@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
正如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。
不完全是一个答案,但可能是一个后续问题(最初的问题不明确):
如果你真的想遵循符号链接,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