Преобразование относительного пути в абсолютный путь? - PullRequest
54 голосов
/ 28 октября 2010

Я не уверен, являются ли эти пути дубликатами.Учитывая относительный путь, как мне определить абсолютный путь, используя скрипт оболочки?

Пример:

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

absolute path: /a/b/c/d

Ответы [ 6 ]

51 голосов
/ 28 октября 2010

Самый надежный метод, с которым я столкнулся в Unix: readlink -f:

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

Несколько предостережений:

  1. Это также имеет побочный эффект разрешениявсе символические ссылки.Это может быть или не быть желательным, но обычно это так.
  2. readlink даст пустой результат, если вы ссылаетесь на несуществующий каталог.Если вы хотите поддерживать несуществующие пути, используйте вместо этого readlink -m.К сожалению, эта опция не существует в версиях readlink, выпущенных до 2005 года.
48 голосов
/ 28 октября 2010

Из приходит этот источник :

#!/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

19 голосов
/ 22 октября 2013

Использование

# 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 не часто устанавливается по умолчанию.Если вы не уверены, что присутствует realpath или readlink, вы можете заменить его с помощью perl (см. Ниже).


Используя

Steven Kramer предлагает псевдоним оболочки, если realpath недоступен в вашей системе:

$ 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 -line команда использует Cwd::realpath.На самом деле существует три функции perl.Они принимают один аргумент и возвращают абсолютный путь.Ниже приведены подробные сведения из документации Perl5> Основные модули> 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, чтобы избежать ошибки «Неопределенная подпрограмма» , как указанона ариэльф .Если вы хотите импортировать все эти три функции, вы можете использовать одну строку use Cwd:

use Cwd qw(abs_path realpath fast_abs_path);
18 голосов
/ 28 октября 2010

Взгляните на 'realpath'.

$ realpath

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

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

/data/home
1 голос
/ 25 декабря 2014

Так как я сталкивался с этим много раз на протяжении многих лет, и в этот раз мне понадобилась чисто переносимая версия bash, которую я мог бы использовать для OSX и Linux, я решил написать одну:

живая версия живет здесь:

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

но ради SO, вот текущая версия (я чувствую, что она хорошо протестирована ... но я открыта для обратной связи!)

Может быть нетрудно заставить его работать на простой оболочке Bourne (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
0 голосов
/ 02 февраля 2012

Может быть, это поможет:

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