将相对路径转换为绝对路径?

Posted

技术标签:

【中文标题】将相对路径转换为绝对路径?【英文标题】:Converting relative path into absolute path? 【发布时间】:2011-05-02 00:13:18 【问题描述】:

我不确定这些路径是否重复。给定相对路径,如何使用 shell 脚本确定绝对路径?

例子:

relative path: /x/y/../../a/b/z/../c/d

absolute path: /a/b/c/d

【问题讨论】:

您的“相对”路径实际上是绝对路径,但不是规范形式。相对路径永远不会以/ 开头。也许搜索“规范路径”。 bash/fish command to print absolute path to a file 的可能重复项 【参考方案1】:

来自this source:

#!/bin/bash

# Assume parameter passed in is a relative path to a directory.
# For brevity, we won't do argument type or length checking.

ABS_PATH=`cd "$1"; pwd` # double quotes for paths that contain spaces etc...
echo "Absolute path: $ABS_PATH"

你也可以做一个 Perl 单行,例如使用Cwd::abs_path

【讨论】:

非常好。如果还给出了文件名怎么办。比如/x/y/../../a/b/z/../c/d.txt => /a/b/c/d.txt 第一个在包含空格的路径上失败(尽管有趣的是,像 foo; echo BAR 这样的路径并没有像我预期的那样行为不端)。 -1 引起你的注意... @j_random_hacker - (1) 已修复; (2) 用户收到新cmets的通知,无需通过投票来获得关注:) +2 :) 我知道,“引起你的注意”是狡猾的词——“供应动机”更像是……我现在把它作为一般政策来做,因为我已经决定了不幸的是,结果好坏参半。 有趣的是,如果指定的路径不存在,Cwd::abs_path 会失败。【参考方案2】:

我在unix中遇到的最可靠的方法是readlink -f

$ readlink -f /x/y/../../a/b/z/../c/d
/a/b/c/d

几个注意事项:

    这也有解决所有符号链接的副作用。这可能是可取的,也可能不是可取的,但通常是这样的。 如果您引用不存在的目录,readlink 将给出空白结果。如果要支持不存在的路径,请改用readlink -m。不幸的是,这个选项在 ~2005 年之前发布的 readlink 版本中不存在。

【讨论】:

readlink -f is not available on OS X. @jared-beck 你能证明readlink -f 做同样的事情吗?在我的(OS X El Capitan)安装中,readlink -f 首先基本上是stat -f,这表明:-f Display information using the specified format. See the FORMATS section for a description of valid formats.。也许你安装了coreutils 目前,OS X El Capitan 有一个不同的 readlink 命令,可以找到符号链接的真实路径,而不是将相对路径解析为绝对路径。因此,如果我们想要可移植,看起来我们只能更改目录然后返回。【参考方案3】:

看看'realpath'。

$ realpath

usage: realpath [-q] path [...]

$ realpath ../../../../../

/data/home

【讨论】:

【参考方案4】:

这可能会有所帮助:

$path = "~user/dir/../file" 
$resolvedPath = glob($path); #   (To resolve paths with '~')
# Since glob does not resolve relative path, we use abs_path 
$absPath      = abs_path($path);

【讨论】:

【参考方案5】:

使用bash

# Directory
relative_dir="folder/subfolder/"
absolute_dir="$( cd "$relative_dir" && pwd )"

# File
relative_file="folder/subfolder/file"
absolute_file="$( cd "$relative_file%/*" && pwd )"/"$relative_file##*/"
$relative_file%/*dirname "$relative_file" 的结果相同 $relative_file##*/basename "$relative_file" 的结果相同

注意事项:不解析符号链接(即不规范化路径)=>如果您使用符号链接,可能无法区分所有重复项。


使用realpath

命令realpath 完成这项工作。另一种方法是使用readlink -e(或readlink -f)。但是 realpath 默认情况下并不经常安装。如果您不能确定 realpathreadlink 是否存在,您可以使用 perl 替换它(见下文)。


使用perl

如果realpath 在您的系统中不可用,Steven Kramer 会建议一个 shell 别名:

$ alias realpath="perl -MCwd -e 'print Cwd::realpath(\$ARGV[0]),qq<\n>'"
$ realpath path/folder/file
/home/user/absolute/path/folder/file

或者如果您更喜欢直接使用 perl:

$ perl -MCwd -e 'print Cwd::realpath($ARGV[0]),qq<\n>' path/folder/file
/home/user/absolute/path/folder/file

这个单行 perl 命令使用Cwd::realpath。实际上有三个 perl 函数。它们采用单个参数并返回绝对路径名。以下详细信息来自文档Perl5 > Core modules > Cwd。

abs_path() 使用与getcwd() 相同的算法。符号链接和相对路径组件(...)被解析为返回规范路径名,就像 realpath

use Cwd 'abs_path';
my $abs_path = abs_path($file);

realpath()abs_path()的同义词

use Cwd 'realpath';
my $abs_path = realpath($file);

fast_abs_path()abs_path() 的一个更危险但可能更快的版本

use Cwd 'fast_abs_path';
my $abs_path = fast_abs_path($file);

这些函数仅在请求时导出 => 因此使用Cwd 来避免arielf 指出的“未定义子例程” 错误。如果要导入所有这三个函数,可以使用单个 use Cwd 行:

use Cwd qw(abs_path realpath fast_abs_path);

【讨论】:

+1,但您能否通过在示例前添加 use Cwd qw(abs_path realpath fast_abs_path); 来使其更完整?照原样,所有 3 个示例都会产生“未定义的子例程”错误。 具有不错的 Perl 但没有安装 realpath 的发行版的单行版本:alias realpath="perl -MCwd -e 'print Cwd::realpath ($ARGV[0]), qq&lt;\n&gt;'" @olibre MacOSX,Perl v5.18.2。脚本地址:gist.github.com/thefosk/43296cb8bf74497c9d69 @Mark 你的shell 在别名的定义中扩展$ARGV,所以Perl sees Cwd::realpath ([0]),这是一个数组引用。在别名中转义 $ARGV。 zsh没有这个问题。 查看我的编辑,它转义了别名,应该适用于 bash/zsh/ksh。【参考方案6】:

由于这些年来我遇到过很多次,而这一次我需要一个可以在 OSX 和 linux 上使用的纯 bash 便携版本,所以我继续写了一个:

活版在这里:

https://github.com/keen99/shell-functions/tree/master/resolve_path

但是为了 SO,这里是当前版本(我觉得它已经过很好的测试......但我愿意接受反馈!)

让它适用于普通的 bourne shell (sh) 可能并不难,但我没有尝试......我太喜欢 $FUNCNAME 了。 :)

#!/bin/bash

resolve_path() 
    #I'm bash only, please!
    # usage:  resolve_path <a file or directory> 
    # follows symlinks and relative paths, returns a full real path
    #
    local owd="$PWD"
    #echo "$FUNCNAME for $1" >&2
    local opath="$1"
    local npath=""
    local obase=$(basename "$opath")
    local odir=$(dirname "$opath")
    if [[ -L "$opath" ]]
    then
    #it's a link.
    #file or directory, we want to cd into it's dir
        cd $odir
    #then extract where the link points.
        npath=$(readlink "$obase")
        #have to -L BEFORE we -f, because -f includes -L :(
        if [[ -L $npath ]]
         then
        #the link points to another symlink, so go follow that.
            resolve_path "$npath"
            #and finish out early, we're done.
            return $?
            #done
        elif [[ -f $npath ]]
        #the link points to a file.
         then
            #get the dir for the new file
            nbase=$(basename $npath)
            npath=$(dirname $npath)
            cd "$npath"
            ndir=$(pwd -P)
            retval=0
            #done
        elif [[ -d $npath ]]
         then
        #the link points to a directory.
            cd "$npath"
            ndir=$(pwd -P)
            retval=0
            #done
        else
            echo "$FUNCNAME: ERROR: unknown condition inside link!!" >&2
            echo "opath [[ $opath ]]" >&2
            echo "npath [[ $npath ]]" >&2
            return 1
        fi
    else
        if ! [[ -e "$opath" ]]
         then
            echo "$FUNCNAME: $opath: No such file or directory" >&2
            return 1
            #and break early
        elif [[ -d "$opath" ]]
         then 
            cd "$opath"
            ndir=$(pwd -P)
            retval=0
            #done
        elif [[ -f "$opath" ]]
         then
            cd $odir
            ndir=$(pwd -P)
            nbase=$(basename "$opath")
            retval=0
            #done
        else
            echo "$FUNCNAME: ERROR: unknown condition outside link!!" >&2
            echo "opath [[ $opath ]]" >&2
            return 1
        fi
    fi
    #now assemble our output
    echo -n "$ndir"
    if [[ "x$nbase:=" != "x" ]]
     then
        echo "/$nbase"
    else 
        echo
    fi
    #now return to where we were
    cd "$owd"
    return $retval

这是一个经典的例子,感谢 brew:

%% ls -l `which mvn`
lrwxr-xr-x  1 draistrick  502  29 Dec 17 10:50 /usr/local/bin/mvn@ -> ../Cellar/maven/3.2.3/bin/mvn

使用这个函数,它会返回-real-路径:

%% cat test.sh
#!/bin/bash
. resolve_path.inc
echo
echo "relative symlinked path:"
which mvn
echo
echo "and the real path:"
resolve_path `which mvn`


%% test.sh

relative symlinked path:
/usr/local/bin/mvn

and the real path:
/usr/local/Cellar/maven/3.2.3/libexec/bin/mvn

【讨论】:

【参考方案7】:

我想使用realpath,但它在我的系统 (macOS) 上不可用,所以我想出了这个脚本:

#!/bin/sh

# NAME
#   absolute_path.sh -- convert relative path into absolute path
#
# SYNOPSYS
#   absolute_path.sh ../relative/path/to/file

echo "$(cd $(dirname $1); pwd)/$(basename $1)"

例子:

./absolute_path.sh ../styles/academy-of-management-review.csl 
/Users/doej/GitHub/styles/academy-of-management-review.csl

【讨论】:

以上是关于将相对路径转换为绝对路径?的主要内容,如果未能解决你的问题,请参考以下文章

aspcms怎么将相对路径改成绝对路径

Python - 任何将相对路径转换为绝对路径的方法?

如何将相对路径转换为 ​​C#/.NET 中的完全限定路径?

将相对路径作为属性文件中的属性

带冒号的 Makefile 绝对路径

Gatsby JSON 中的绝对图像路径