我想转换/foo/bar/..
到/foo
是否有一个bash命令执行此操作?
编辑:在我的实际案例中,目录确实存在.
如果你想从路径中选择部分文件名,"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
备择方案
如果realpath
shell不支持,您可以尝试
readlink -f /path/here/..
也
readlink -m /path/there/../../
工作原理与之相同
realpath -s /path/here/../../
因为路径不需要存在才能被标准化.
我不知道是否有直接的bash命令来执行此操作,但我通常会这样做
normalDir="`cd "${dirToNormalize}";pwd`" echo "${normalDir}"
而且运作良好.
试试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
#include
#include
#include
#include
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);
}
一个便携而可靠的解决方案是使用python,它几乎无处不在(包括Darwin)预装.您有两种选择:
abspath
返回绝对路径但不解析符号链接:
python -c "import os,sys; print os.path.abspath(sys.argv[1])" path/to/file
realpath
返回绝对路径,这样做可以解析符号链接,生成规范路径:
python -c "import os,sys; print os.path.realpath(sys.argv[1])" path/to/file
在每种情况下,path/to/file
可以是相对路径或绝对路径.
使用coreutils包中的readlink实用程序.
MY_PATH=$(readlink -f "$0")
readlink
是获得绝对路径的bash标准.如果路径或路径不存在,它还具有返回空字符串的优点(给定标志来执行此操作).
要获取可能存在或可能不存在的目录的绝对路径,但是父项确实存在,请使用:
abspath=$(readlink -f $path)
获取必须与所有父项一起存在的目录的绝对路径:
abspath=$(readlink -e $path)
规范化给定路径并遵循符号链接(如果它们碰巧存在),否则忽略丢失的目录并且只返回路径,它是:
abspath=$(readlink -m $path)
唯一的缺点是readlink将遵循链接.如果您不想关注链接,可以使用此替代约定:
abspath=$(cd ${path%/*} && echo $PWD/${path##*/})
这将chd到$ path的目录部分并打印当前目录以及$ path的文件部分.如果它没有chdir,你得到一个空字符串和stderr错误.
旧问题,但如果您在shell级别处理完整路径名称,则有更简单的方法:
abspath="$( cd "$path" && pwd )"
由于cd发生在子shell中,因此不会影响主脚本.
假设您的shell内置命令接受-L和-P,有两种变体:
abspath="$( cd -P "$path" && pwd -P )" #physical path with resolved symlinks abspath="$( cd -L "$path" && pwd -L )" #logical path preserving symlinks
就个人而言,我很少需要这种后来的方法,除非我出于某种原因着迷于符号链接.
仅供参考:获取脚本起始目录的变化即使脚本稍后更改其当前目录也能正常工作.
name0="$(basename "$0")"; #base name of script dir0="$( cd "$( dirname "$0" )" && pwd )"; #absolute starting dir
使用CD可以确保您始终拥有绝对目录,即使脚本是由./script.sh等命令运行的,如果没有cd/pwd,通常只会给出..如果脚本稍后执行cd,则无用.
正如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
.
我最近的解决方案是:
pushd foo/bar/.. dir=`pwd` popd
根据Tim Whitcomb的回答.
不完全是一个答案,但可能是一个后续问题(原始问题不明确):
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