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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/284662/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 16:43:37  来源:igfitidea点击:

How do you normalize a file path in Bash?

linuxbashunixshell

提问by Fabien

I want to transform /foo/bar/..to /foo

我想转变/foo/bar/../foo

Is there a bash command which does this?

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



Edit: in my practical case, the directory does exist.

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

采纳答案by Kent Fredric

if you're wanting to chomp part of a filename from the path, "dirname" and "basename" are your friends, and "realpath" is handy too.

如果你想从路径中截取文件名的一部分,“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

realpathalternatives

realpath备择方案

If realpathis not supported by your shell, you can try

如果realpath您的外壳不支持,您可以尝试

readlink -f /path/here/.. 

Also

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

Works the same as

工作原理与

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

in that the path doesn't need to exist to be normalized.

因为路径不需要存在即可标准化。

回答by Adam Liss

Try realpath. Below is the source in its entirety, hereby donated to the public domain.

试试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);
}

回答by Tim Whitcomb

I don't know if there is a direct bash command to do this, but I usually do

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

normalDir="`cd "${dirToNormalize}";pwd`"
echo "${normalDir}"

and it works well.

它运作良好。

回答by alhernau

Talkative, and a bit late answer. I need to write one since I'm stuck on older RHEL4/5. I handles absolute and relative links, and simplifies //, /./ and somedir/../ entries.

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

test -x /usr/bin/readlink || readlink () {
        echo $(/bin/ls -l  | /bin/cut -d'>' -f 2)
    }


test -x /usr/bin/realpath || realpath () {
    local PATH=/bin:/usr/bin
    local inputpath=
    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`

回答by mattalxndr

Use the readlink utility from the coreutils package.

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

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

回答by loevborg

A portable and reliable solution is to use python, which is preinstalled pretty much everywhere (including Darwin). You have two options:

一个可移植且可靠的解决方案是使用 python,它几乎在任何地方(包括 Darwin)都预装。您有两个选择:

  1. abspathreturns an absolute path but does not resolve symlinks:

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

  2. realpathreturns an absolute path and in doing so resolves symlinks, generating a canonical path:

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

  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

In each case, path/to/filecan be either a relative or absolute path.

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

回答by Jeet

As Adam Liss noted realpathis not bundled with every distribution. Which is a shame, because it is the best solution. The provided source code is great, and I will probably start using it now. Here is what I have been using until now, which I share here just for completeness:

正如 Adam Liss 所指出的realpath,并不是每个发行版都捆绑在一起。这是一种耻辱,因为它是最好的解决方案。提供的源代码很棒,我现在可能会开始使用它。这是我到目前为止一直在使用的内容,为了完整起见,我在这里分享:

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

If you want it to resolve symlinks, just replace pwdwith pwd -P.

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

回答by schmunk

My recent solution was:

我最近的解决方案是:

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

Based on the answer of Tim Whitcomb.

基于 Tim Whitcomb 的回答。

回答by Jesse Glick

Not exactly an answer but perhaps a follow-up question (original question was not explicit):

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

readlinkis fine if you actually want to follow symlinks. But there is also a use case for merely normalizing ./and ../and //sequences, which can be done purely syntactically, withoutcanonicalizing symlinks. readlinkis no good for this, and neither is realpath.

readlink如果您确实想遵循符号链接,那很好。但也有一个用例仅规范化./and..///序列,这可以纯粹在语法上完成,而无需规范化符号链接。readlink对这没有好处,也不是realpath

jrunscript -e 'for (var i = 0; i < arguments.length; i++) {println(new java.io.File(new java.io.File(arguments[i]).toURI().normalize()))}' $paths

works for existing paths, but breaks for others.

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

A sedscript would seem to be a good bet, except that you cannot iteratively replace sequences (/foo/bar/baz/../..-> /foo/bar/..-> /foo) without using something like Perl, which is not safe to assume on all systems, or using some ugly loop to compare the output of sedto its input.

一个sed脚本似乎是一个不错的选择,但你不能反复更换序列(/foo/bar/baz/../..- > /foo/bar/..- > /foo),而不使用像Perl中,这是不是安全地假定所有系统上,或者使用一些丑陋的环路的输出比较sed来它的输入。

FWIW, a one-liner using Java (JDK 6+):

FWIW,使用 Java (JDK 6+) 的单线:

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

回答by apottere

I'm late to the party, but this is the solution I've crafted after reading a bunch of threads like this:

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

##代码##

This will resolve the absolute path of $1, play nice with ~, keep symlinks in the path where they are, and it won't mess with your directory stack. It returns the full path or nothing if it doesn't exist. It expects $1 to be a directory and will probably fail if it's not, but that's an easy check to do yourself.

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