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

时间:2010-10-28 16:56:10

标签: perl unix shell path absolute-path

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

示例:

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

absolute path: /a/b/c/d

7 个答案:

答案 0 :(得分:51)

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

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

一些警告:

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

答案 1 :(得分:48)

来自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

答案 2 :(得分:18)

看看'realpath'。

$ realpath

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

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

/data/home

答案 3 :(得分:18)

使用

# 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替换它(见下文)。


使用

如果您的系统中没有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);
    

这些功能仅在request =&gt;导出时导出。因此,使用Cwd来避免arielf指出的“Undefined subroutine”错误。如果要导入所有这三个函数,可以使用单个use Cwd行:

use Cwd qw(abs_path realpath fast_abs_path);

答案 4 :(得分:1)

由于我多年来遇到过这么多次,而这次我需要一个可以在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- path:

%% 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

答案 5 :(得分:0)

可能有帮助:

$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);

答案 6 :(得分:0)

我想使用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