Преобразование относительного пути в абсолютный путь?

48

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

Пример:

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

absolute path: /a/b/c/d
  • 11
    Ваш «относительный» путь на самом деле является абсолютным, но не в канонической форме. Относительный путь никогда не начинается с / . Может быть, поиск так "канонический путь".
  • 0
    возможный дубликат команды bash / fish для вывода абсолютного пути к файлу
Теги:
path
absolute-path

6 ответов

41
Лучший ответ

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

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

  • 3
    Очень хорошо. И что, если имя файла также дается. как /x/y/../../a/b/z/../c/d.txt => /a/b/c/d.txt
  • 4
    Обновлен решением Perl, которое работает с файлами
Показать ещё 8 комментариев
48

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

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

Пара предупреждений:

  • Это также имеет побочный эффект разрешения всех символических ссылок. Это может быть или не быть желательным, но обычно это.
  • readlink даст пустой результат, если вы ссылаетесь на несуществующий каталог. Если вы хотите поддерживать несуществующие пути, используйте readlink -m. К сожалению, этот вариант не существует в версиях readlink, выпущенных до ~ 2005 года.
  • 7
    readlink -f не доступен в OS X
  • 1
    Теперь (2015) readlink -f доступна на OS X
Показать ещё 2 комментария
18

Взгляните на "реальный путь".

$ realpath

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

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

/data/home
14

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

# 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 (см. Ниже).


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

Стивен Крамер предлагает псевдоним оболочки, если 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 использует 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, чтобы избежать ошибки подпрограммы Undefined, как указано arielf. Если вы хотите импортировать все эти три функции, вы можете использовать одну строку use Cwd:

use Cwd qw(abs_path realpath fast_abs_path);
  • 0
    +1, но не могли бы вы сделать это более полным, добавив use Cwd qw(abs_path realpath fast_abs_path); перед примерами? Как и все, все 3 примера выдают ошибки «Неопределенная подпрограмма».
  • 0
    Большое спасибо @arielf за ваше предложение. Я надеюсь, что переработанный ответ соответствует вашей мечте :-) Какие-то другие улучшения? Ура ;-)
Показать ещё 9 комментариев
1

Поскольку я сталкивался с этим много раз с годами, и на этот раз мне нужна чистая портативная версия 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 a link.
    #file or directory, we want to cd into it 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
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);

Ещё вопросы

Сообщество Overcoder
Наверх
Меню