你如何正常化Bash中的文件path?

我想将/foo/bar/../foo

有没有这样做的bash命令?


编辑:在我的实际情况下,目录确实存在。

如果你想从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 

编辑

实际path似乎不是标准问题。

最接近你可以得到的股票标准是

 readlink -f /path/here/.. 

Realpath似乎来自debian,不是coreutils的一部分: http ://packages.debian.org/unstable/utils/realpath原来是DWWW包的一部分。

(也可以在应用程序pipe理/实时pathgentoo)

 readlink -m /path/there/../../ 

和。一样工作

  realpath -s /path/here/../../ 

因为它不需要实际存在的path来规范化它。

我不知道是否有一个直接的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 <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); } 

使用coreutils软件包中的readlink实用程序。

 MY_PATH=$(readlink -f "$0") 

一个便携和可靠的解决scheme是使用python,它预装在几乎所有地方(包括达尔文)。 你有两个select:

  1. abspath返回绝对path,但不能parsing符号链接:

    python -c "import os,sys; print os.path.abspath(sys.argv[1])" path/to/file

  2. realpath返回一个绝对path,并在这样做parsing符号链接,生成一个规范的path:

    python -c "import os,sys; print os.path.realpath(sys.argv[1])" path/to/file

在每种情况下, path/to/file可以是相对path或绝对path。

readlink是获取绝对path的bash标准。 它还具有返回空string的优点,如果path或path不存在(给定的标志这样做)。

要获得可能存在或不存在的目录的绝对path,但是父母确实存在,请使用:

 abspath=$(readlink -f $path) 

为了获得必须与所有父母一起存在的目录的绝对path:

 abspath=$(readlink -e $path) 

要规范化给定的path,并遵循符号链接,如果他们碰巧存在,但否则忽略丢失的目录,只是返回path,它是:

 abspath=$(readlink -m $path) 

唯一的缺点是readlink会跟随链接。 如果你不想跟随链接,你可以使用这个替代的约定:

 abspath=$(cd ${path%/*} && echo $PWD/${path##*/}) 

这将chdir到$ path的目录部分,并打印当前目录以及$ path的文件部分。 如果chdir失败,你会得到一个空string,并在stderr上出错。

我最近的解决scheme是:

 pushd foo/bar/.. dir=`pwd` popd 

基于Tim Whitcomb的回答。

正如Adam Liss指出的, realpath并不是与每个发行版捆绑在一起的。 这是一个耻辱,因为这是最好的解决scheme。 提供的源代码非常好,现在我可能会开始使用它。 以下是我迄今为止所使用的内容,我只是为了完整而分享这些内容:

 get_abs_path() { local PARENT_DIR=$(dirname "$1") cd "$PARENT_DIR" local ABS_PATH="$(pwd)"/"$(basename "$1")" cd - >/dev/null echo "$ABS_PATH" } 

如果你想要parsing符号链接,只需用pwd -Preplacepwd

健谈,还有一点迟到的答案。 我需要写一个,因为我被困在较旧的RHEL4 / 5上。 我处理绝对和相对链接,并简化//,/./和somedir /../条目。

 test -x /usr/bin/readlink || readlink () { echo $(/bin/ls -l $1 | /bin/cut -d'>' -f 2) } test -x /usr/bin/realpath || realpath () { local PATH=/bin:/usr/bin local inputpath=$1 local changemade=1 while [ $changemade -ne 0 ] do changemade=0 local realpath="" local token= for token in ${inputpath//\// } do case $token in ""|".") # noop ;; "..") # up one directory changemade=1 realpath=$(dirname $realpath) ;; *) if [ -h $realpath/$token ] then changemade=1 target=`readlink $realpath/$token` if [ "${target:0:1}" = '/' ] then realpath=$target else realpath="$realpath/$target" fi else realpath="$realpath/$token" fi ;; esac done inputpath=$realpath done echo $realpath } mkdir -p /tmp/bar (cd /tmp ; ln -s /tmp/bar foo; ln -s ../.././usr /tmp/bar/link2usr) echo `realpath /tmp/foo` 

不完全是一个答案,但也许是一个后续问题(原始问题并不明确):

如果你真的想遵循符号链接, readlink是好的。 但是也有一个用例,它只是对./..///序列进行规范化,可以在语法上纯粹地完成, 而不需要规范化符号链接。 readlink对此并不好,也不是readlink

 for f in $paths; do (cd $f; pwd); done 

为现有的path工作,但为别人打破。

一个sed脚本似乎是一个很好的select,除了你不能使用像Perl这样的东西来迭代地replace序列( /foo/bar/.. /foo/bar/baz/../.. > /foo/bar/.. /foo/bar/baz/../.. > /foo ) ,这在所有系统上都是不安全的,或者使用一些丑陋的循环来比较sed的输出和它的input。

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 

我迟到了,但是这是我读过一堆这样的线程后制作的解决scheme:

 resolve_dir() { (builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"` 2>/dev/null; if [ $? -eq 0 ]; then pwd; fi) } 

这将解决$ 1的绝对path,和〜一起玩,在它们所在的path上保留符号链接,并且不会混淆你的目录栈。 它返回完整的path,或者如果不存在,则返回任何内容。 它预计1美元是一个目录,如果不是,可能会失败,但这是一个简单的检查自己做。

旧的问题,但是如果你在shell级别处理完整的path名 ,则有更简单的方法:

  abspath =“$(cd”$ path“&& pwd)” 

由于cd发生在一个子shell中,它不会影响主脚本。

假设您的shell内置命令接受-L和-P,两个变体是:

    abspath =“$(cd -P”$ path“&& pwd -P)”#已parsing符号链接的#物理path
    abspath =“$(cd -L”$ path“&& pwd -L)”#逻辑path保留符号链接

就个人而言,我很less需要这种方法,除非我因某种原因而着迷于符号链接。

仅供参考:即使脚本稍后改变其当前目录,也可以获得脚本的起始目录。

  name0 =“$(basename”$ 0“)”;  #脚本的基本名称
 dir0 =“$(cd”$(dirname“$ 0”)“&& pwd)”;  #absolute开始目录 

CD的使用保证你总是拥有绝对的目录,即使脚本是通过诸如./script.sh之类的命令来运行的,而没有cd / pwd的话,它经常会给出这个目录。

试试我们已经放在GitHub上的新的Bash库产品realpath-lib ,免费和无阻碍的使用。 它被彻底logging,并成为一个伟大的学习工具。

它解决了局部的,相对的和绝对的path,除了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。 尝试跨平台,并帮助改善它。

实际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 } 

注意这个变体也不需要存在的path。

基于@安德烈的回答,我可能会有一个稍微好一点的版本,以防有人在无循环,完全string操作的解决scheme之后。 对于那些不想解引用任何符号链接的人来说也是有用的,这是使用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 

今天我发现你可以使用stat命令来parsingpath。

所以对于像“〜/ Documents”这样的目录:

你可以运行这个:

stat -f %N ~/Documents

要获得完整path:

/Users/me/Documents

对于符号链接,可以使用%Y格式选项:

stat -f %Y example_symlink

其中可能会返回如下结果:

/usr/local/sbin/example_symlink

在其他版本的* NIX格式化选项可能会有所不同,但这些在OSX上为我工作。

基于loveborg的优秀python片段,我写这个:

 #!/bin/sh # Version of readlink that follows links to the end; good for Mac OS X for file in "$@"; do while [ -h "$file" ]; do l=`readlink $file` case "$l" in /*) file="$l";; *) file=`dirname "$file"`/"$l" esac done #echo $file python -c "import os,sys; print os.path.abspath(sys.argv[1])" "$file" done 

一个简单的解决scheme,使用node.js

 #!/usr/bin/env node process.stdout.write(require('path').resolve(process.argv[2]));