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

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

Пример:

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

absolute path: /a/b/c/d

person josh    schedule 28.10.2010    source источник
comment
Ваш относительный путь на самом деле является абсолютным путем, но не в канонической форме. Относительный путь никогда не начинается с /. Может быть, ищите ТАК канонический путь.   -  person Stephen P    schedule 28.10.2010
comment
возможный дубликат команды bash / fish для вывода абсолютного пути к файл   -  person Trevor Boyd Smith    schedule 05.05.2015


Ответы (7)


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

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

person DVK    schedule 28.10.2010
comment
Очень хорошо. А что, если также указано имя файла. например /x/y/../../a/b/z/../c/d.txt = ›/a/b/c/d.txt - person josh; 28.10.2010
comment
Первый не работает на путях, содержащих пробелы (хотя, что интересно, такой путь, как foo; echo BAR, не ведет себя так, как я ожидал). -1 чтобы привлечь ваше внимание ... - person j_random_hacker; 03.02.2011
comment
@j_random_hacker - (1) исправлено; (2) пользователи получают уведомления о новых комментариях к своим ответам, не нужно использовать голоса, чтобы привлечь внимание :) - person DVK; 03.02.2011
comment
+2 :) Я знаю, обратите внимание, это ласковые слова - мотивация предложения больше похожа на это ... Сейчас я делаю это как общую политику, так как, к сожалению, у меня явно смешанные результаты с простыми комментариями. - person j_random_hacker; 03.02.2011
comment
Интересно, что Cwd::abs_path завершится ошибкой, если указанный путь не существует. - person RJFalconer; 26.02.2013
comment
@oberlies - хотя вы, конечно, можете голосовать по любым причинам, по каким пожелаете, несогласие с совершенно подходящим и стандартным стилем форматирования SO обычно не считается одной из подходящих причин для голос против. Не стесняйтесь спрашивать на Meta, если вы не согласны. - person DVK; 13.06.2013
comment
@oberlies - на самом деле, кто-то - предположительно вы - сегодня пытался отредактировать сообщение дважды, и изменения, по-видимому, НЕ были одобрены (поскольку сообщение не редактировалось) - это означает, что два отдельных Рецензенты, которых не я, полностью не согласны с вашим мнением о том, что вы предпочитаете лучше. - person DVK; 13.06.2013
comment
cd "$1"; pwd - это очень плохо, потому что если cd завершится неудачей (если $1 не является допустимым каталогом), тогда pwd предоставит текущий путь, а ABS_PATH будет содержать сообщение об ошибке от cd и текущий путь (да!). Более стабильный код _7 _ && `гарантирует, что pwd будет выполняться тогда и только тогда, когда cd успешно, иначе ABS_PATH будет просто пустым. - person LaurentValade; 11.09.2020

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

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

Пара предостережений:

  1. Это также имеет побочный эффект разрешения всех символических ссылок. Это может быть, а может и не быть желательным, но обычно так и есть.
  2. readlink даст пустой результат, если вы укажете несуществующий каталог. Если вы хотите поддерживать несуществующие пути, используйте вместо этого readlink -m. К сожалению, этой опции нет в версиях readlink, выпущенных до ~ 2005 г.
person bukzor    schedule 28.10.2010
comment
comment
@ 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? - person Kreisquadratur; 27.11.2015
comment
В настоящее время в OS X El Capitan есть другая команда readlink, которая может найти реальный путь символьной ссылки, а не преобразовывать относительные пути в абсолютные. Итак, если мы хотим быть переносимыми, похоже, что мы застряли на смене каталога, а затем возвращаемся. - person Spencer Williams; 18.02.2016

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


Использование 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> Core modules> Cwd.

  • abs_path() использует тот же алгоритм, что и getcwd(). Символические ссылки и компоненты относительного пути (. и ..) разрешены так, чтобы возвращать канонический путь, как и _ 21_.

    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);
person oHo    schedule 22.10.2013
comment
+1, но не могли бы вы сделать это более полным, добавив use Cwd qw(abs_path realpath fast_abs_path); перед примерами? Как есть, все 3 примера приводят к неопределенным ошибкам подпрограммы. - person arielf - Reinstate Monica; 06.08.2014
comment
Большое спасибо @arielf за ваше предложение. Надеюсь, переработанный ответ соответствует вашей мечте :-) Есть ли другие улучшения? Ваше здоровье ;-) - person oHo; 28.08.2014
comment
Однострочная версия для дистрибутивов с приличным Perl, но без realpath установки: alias realpath="perl -MCwd -e 'print Cwd::realpath ($ARGV[0]), qq<\n>'" - person Steven Kramer; 25.09.2014
comment
Благодарим за идею использовать perl в качестве замены realpath. Я интегрировал вашу однострочную команду в ответ, чтобы она была полезна другим читателям SO. Ура ;-) (Я также проголосовал за некоторые из ваших ответов в качестве награды) - person oHo; 26.09.2014
comment
@StevenKramer @olibre Это возвращает такой результат: /etc/ARRAY(0x9afa68) вместо /etc/file, как я могу распечатать имя файла вместо дампа ARRAY? - person Mark; 20.02.2015
comment
Привет Марк. Какая у вас версия Perl? Какой у вас дистрибутив Linux? (или BSD / MAC / Windows ...)? Пожалуйста, предоставьте пример оболочки, включая создание файла, чтобы воспроизвести вашу проблему. Ваше здоровье ;-) - person oHo; 20.02.2015
comment
@olibre MacOSX, Perl v5.18.2. Скрипт по адресу: gist.github.com/thefosk/43296cb8bf74497c9d69 - person Mark; 21.02.2015
comment
@Mark ваша оболочка расширяет $ARGV в определении псевдонима, так что Perl sees Cwd::realpath ([0]), который является ссылкой на массив. Escape $ARGV в псевдониме. zsh не имеет этой проблемы. - person Steven Kramer; 07.03.2015
comment
Спасибо @StevenKramer за ваш вклад (я не смог воспроизвести проблему) :-) Мне, возможно, придется улучшить ответ, если это может происходить часто ... Вы предлагаете что-нибудь, чтобы улучшить ответ ... Хороших выходных . Ваше здоровье. - person oHo; 07.03.2015
comment
См. Мою правку, которая экранирует псевдоним и должна работать для bash / zsh / ksh. - person Steven Kramer; 08.03.2015
comment
Отлично :) Я проголосую за некоторые из ваших ответов, чтобы вознаградить вас, так как этот ответ также ваш;) Ура - person oHo; 09.03.2015

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

$ realpath

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

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

/data/home
person SteveMc    schedule 28.10.2010

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

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

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

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

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

вот классический пример, благодаря пиву:

%% 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
person keen    schedule 24.12.2014

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

$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);
person Rohan    schedule 02.02.2012

Я хотел использовать 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
person customcommander    schedule 05.05.2020