如何在Bash中规范化文件路径?

Fab*_*ien 188 unix linux bash shell

我想转换/foo/bar/../foo

是否有一个bash命令执行此操作?


编辑:在我的实际案例中,目录确实存在.

Ken*_*ric 178

如果你想从路径中选择部分文件名,"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
Run Code Online (Sandbox Code Playgroud)

realpath 备择方案

如果realpathshell不支持,您可以尝试

readlink -f /path/here/.. 
Run Code Online (Sandbox Code Playgroud)

readlink -m /path/there/../../ 
Run Code Online (Sandbox Code Playgroud)

工作原理与之相同

realpath -s /path/here/../../
Run Code Online (Sandbox Code Playgroud)

因为路径不需要存在才能被标准化.

  • 对于那些需要OS X解决方案的人,请查看下面的Adam Liss的回答. (4认同)

Tim*_*omb 95

我不知道是否有直接的bash命令来执行此操作,但我通常会这样做

normalDir="`cd "${dirToNormalize}";pwd`"
echo "${normalDir}"
Run Code Online (Sandbox Code Playgroud)

而且运作良好.

  • 这将标准化但不能解析软链接.这可能是错误或功能.:-) (9认同)
  • 蒂姆的答案绝对是最简单,最便携的.CDPATH很容易处理:dir ="$(unset CDPATH && cd"$ dir"&& pwd)" (7认同)
  • 如果定义了$ CDPATH,它也会出现问题; 因为"cd foo"将切换到任何"foo"目录,该目录是$ CDPATH的子目录,而不仅仅是当前目录中的"foo".我认为你需要做类似的事情:CDPATH =""cd"$ {dirToNormalize}"&& pwd -P. (4认同)
  • 是的,根据@ DavidBlevins的评论,最好使用"&&". (4认同)
  • 如果你执行`pwd -P`,它将解析符号链接 (4认同)
  • 如果dirToNormalize不存在,这可能是非常危险的(`rm -rf $ normalDir`)! (2认同)

Ada*_*iss 53

试试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);
}
Run Code Online (Sandbox Code Playgroud)

  • 它是ubuntu/BSD的标准,而不是Centos/OSX (7认同)
  • 这也不是OS X上的标准命令. (6认同)
  • 我没有在我使用过的任何系统上看到过这个命令. (4认同)
  • http://www.gnu.org/software/coreutils/ for readlink,但是realpath来自这个包:http://packages.debian.org/unstable/utils/realpath (4认同)
  • 你应该用"Bonus:它是一个bash命令,并随处可用!"来完成这个部分.部分关于`realpath`.它也恰好是我的最爱,但不是从源代码中编译你的工具.这一切都是重量级的,大多数时候你只需要`readlink -f` ... BTW,`readlink`也不是内置的bash,而是Ubuntu上`coreutils`的一部分. (4认同)
  • 您已经说过要将此捐赠给公共领域,但标题也说"对于任何非营利性用途" - 您是否真的想将其捐赠给公共领域?这意味着它可以用于商业营利目的以及非盈利性...... (2认同)
  • 构建 OS X 的步骤: (1) 从该页面复制代码 (2) 将代码粘贴到文件中:pbpaste &gt; realpath.c (3) 构建它:gcc -o realpath realpath.c (4) 运行它:./真实路径路径/到/制作/规范 (2认同)

loe*_*org 35

一个便携而可靠的解决方案是使用python,它几乎无处不在(包括Darwin)预装.您有两种选择:

  1. abspath 返回绝对路径但不解析符号链接:

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

  2. realpath 返回绝对路径,这样做可以解析符号链接,生成规范路径:

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

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

  • 谢谢,这是唯一有效的.OS X下不提供readlink或realpath.Python应该在大多数平台上. (6认同)
  • 我真的很难以理解,如果你不想关注链接,这是唯一理智的解决方案.Unix方式我的FOOT. (2认同)

mat*_*ndr 34

使用coreutils包中的readlink实用程序.

MY_PATH=$(readlink -f "$0")
Run Code Online (Sandbox Code Playgroud)

  • BSD 没有 -f 标志,这意味着即使在最新的 MacOS Mojave 和许多其他系统上也会失败。如果您想要可移植性,请忘记使用 -f,许多操作系统都会受到影响。 (3认同)
  • @索林。问题不在于 Mac,而在于 Linux。 (2认同)

Cra*_*aig 13

readlink是获得绝对路径的bash标准.如果路径或路径不存在,它还具有返回空字符串的优点(给定标志来执行此操作).

要获取可能存在或可能不存在的目录的绝对路径,但是父项确实存在,请使用:

abspath=$(readlink -f $path)
Run Code Online (Sandbox Code Playgroud)

获取必须与所有父项一起存在的目录的绝对路径:

abspath=$(readlink -e $path)
Run Code Online (Sandbox Code Playgroud)

规范化给定路径并遵循符号链接(如果它们碰巧存在),否则忽略丢失的目录并且只返回路径,它是:

abspath=$(readlink -m $path)
Run Code Online (Sandbox Code Playgroud)

唯一的缺点是readlink将遵循链接.如果您不想关注链接,可以使用此替代约定:

abspath=$(cd ${path%/*} && echo $PWD/${path##*/})
Run Code Online (Sandbox Code Playgroud)

这将chd到$ path的目录部分并打印当前目录以及$ path的文件部分.如果它没有chdir,你得到一个空字符串和stderr错误.

  • `readlink`是一个很好的选择,如果它可用.OS X版本不支持`-e`或`-f`选项.在前三个示例中,您应该在`$ path`周围使用双引号来处理文件名中的空格或通配符.+1用于参数扩展,但这存在安全漏洞.如果`path`为空,这将`cd`到你的主目录.你需要双引号.`abspath = $(cd"$ {path%/*}"&& echo"$ PWD/$ {path ##*/}") (6认同)
  • 这并不意味着任何冒犯,但指出这样的模糊问题很重要.我只想快速回答这个琐碎的问题,如果不是上面的评论,我会相信任何/所有输入的代码.在这些bash讨论中支持OS-X也很重要.很遗憾OS-X上不支持很多命令,许多论坛在讨论Bash时认为这是理所当然的,这意味着我们将继续获得大量的跨平台问题,除非它尽早处理. (5认同)

Gil*_*ert 11

旧问题,但如果您在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,则无用.


Jee*_*eet 7

正如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"
} 
Run Code Online (Sandbox Code Playgroud)

如果您希望它解决符号链接,只需替换pwdpwd -P.


sch*_*unk 7

我最近的解决方案是:

pushd foo/bar/..
dir=`pwd`
popd
Run Code Online (Sandbox Code Playgroud)

根据Tim Whitcomb的回答.


Jes*_*ick 5

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

readlink如果你真的想要遵循符号链接,那很好.但也有一个用例仅仅是正火./..///序列,其可以是纯语法上完成,无需进行规范化的符号链接.readlink对此没有好处,也没有realpath.

for f in $paths; do (cd $f; pwd); done
Run Code Online (Sandbox Code Playgroud)

适用于现有路径,但适用于其他路径.

一个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
Run Code Online (Sandbox Code Playgroud)


apo*_*ere 5

我迟到了,但这是我在阅读了一堆这样的线程后精心制作的解决方案:

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

这将解析 $1 的绝对路径,与 ~ 一起玩,将符号链接保留在它们所在的路径中,并且不会弄乱您的目录堆栈。如果不存在,则返回完整路径或不返回任何内容。它期望 $1 是一个目录,如果不是,则可能会失败,但您自己可以轻松检查。