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

63

Существует ли команда Unix для получения абсолютного (и канонизированного) пути из относительного пути, который может содержать символические ссылки?

Вениамин
источник

Ответы:

80

Вы можете использовать readlinkутилиту с -fопцией:

-f, --canonicalize

      canonicalize  by  following  every symlink in every component of
      the given name recursively; all  but  the  last  component  must
      exist

Некоторые дистрибутивы, например те, которые используют GNU coreutils и FreeBSD , также поставляются с realpath(1)утилитой, которая в основном просто вызывает realpath(3)и делает почти то же самое.

Мат
источник
17
-fПереключатель не существует на Mac OS X (по крайней мере , по состоянию на 10.8.5). Без -fпереключателя он просто возвращает код ошибки.
иконоборчество
4
В некоторых системах нет readlinkни realpathSolaris, ни HP-UX, в частности.
Сильдорет
Для проблемы Mac: brew install coreutils apple.stackexchange.com/a/69332/23040 , и если вы не хотите делать шаг, который перенаправляет все стандартные инструменты CLI Mac к недавно установленным эквивалентам GNU, просто позвоните greadlink.
Адриан
19

Портативно, PWDпеременная устанавливается оболочкой в ​​одно абсолютное местоположение текущего каталога. Любой компонент этого пути может быть символической ссылкой.

case $f in
  /*) absolute=$f;;
  *) absolute=$PWD/$f;;
esac

Если вы хотите устранить .и ..как хорошо, перейдите в каталог , содержащий файл и получить $PWDтам:

if [ -d "$f" ]; then f=$f/.; fi
absolute=$(cd "$(dirname -- "$f")"; printf %s. "$PWD")
absolute=${absolute%?}
absolute=$absolute/${f##*/}

Нет переносимого способа перейти по символическим ссылкам. Если у вас есть путь к каталогу, то в большинстве устройств $(cd -- "$dir" && pwd -P 2>/dev/null || pwd)есть путь, который не использует символические ссылки, потому что оболочки, которые отслеживают символические ссылки, имеют тенденцию реализовываться pwd -P(«P» для «физического»).

Некоторые устройства предоставляют утилиту для печати «физического» пути к файлу.

  • Разумно недавние системы Linux (с GNU coreutils или BusyBox) имеют readlink -f, как и FreeBSD ≥8.3, NetBSD ≥4.0 и OpenBSD еще до 2.2.
  • FreeBSD ≥4.3 имеет realpath(он также присутствует в некоторых системах Linux и в BusyBox).
  • Если Perl доступен, вы можете использовать Cwdмодуль .

    perl -MCwd -e 'print Cwd::realpath($ARGV[0])' path/to/file
Жиль "ТАК - перестань быть злым"
источник
Почему вы что-то делаете в pwd( $(cd -- "$dir" && pwd -P 2>/dev/null | pwd))? Кажется, это не заботится о stdin.
Матеуш Пиотровский
1
@MateuszPiotrowski Опечатка, я хотел написать||
Жиль "ТАК - перестань быть злым"
5

Является ли pwdподходит для ваших нужд? Это дает абсолютный путь к текущему каталогу. Или, может быть, вам нужен realpath () .

xanpeng
источник
3

alisterи rss67в этой статье представим наиболее стабильный, совместимый и простой способ. Я никогда не видел лучшего способа, чем этот.

RELPATH=./../
cd $RELPATH
ABSPATH=`pwd`

Если вы хотите вернуться в исходное местоположение,

ORGPATH=`pwd`
RELPATH=./../
cd $RELPATH
ABSPATH=`pwd`
cd $ORGPATH

или же

RELPATH=./../
cd $RELPATH
ABSPATH=`pwd`
cd -

Я хотел бы, чтобы это помогло. Это было лучшее решение для меня.

Eonil
источник
12
Это на самом деле не такой хороший способ. Изменение каталога и его возврат не надежны: у вас может не быть разрешения на возврат, или каталог может быть переименован за это время. Вместо этого, изменить каталоги в субоболочке: ABSPATH=$(cd -- "$RELPATH" && pwd). Обратите внимание на двойные кавычки вокруг подстановки (чтобы не разрывать, если путь содержит пробелы и символы с пробелами) и --(в случае, если путь начинается с -). Также это работает только для каталогов и не канонизирует символические ссылки в соответствии с запросом. Смотрите мой ответ для более подробной информации.
Жиль "ТАК - перестань быть злым"
"> у вас может не быть разрешения вернуться" Вы имеете ввиду cd для dir, но не можете cd back? Не могли бы вы привести несколько примеров сценариев.
Talespin_Kit
0

Другие ответы здесь были хороши, но были недостаточны для моих нужд. Мне нужно было решение, которое я мог бы использовать в своих скриптах на любом компьютере. Моим решением было написать сценарий оболочки, который я могу вызывать из сценариев, где он мне нужен.

#!/bin/sh
if [ $# -eq 0 ] || [ $# -gt 2 ]; then
  printf 'Usage: respath path [working-directory]\n' >&2
  exit 1
fi
cantGoUp=

path=$1
if [ $# -gt 1 ]; then
  cd "$2"
fi
cwd=`pwd -P` #Use -P option to account for directories that are actually symlinks

#Handle non-relative paths, which don't need resolution
if echo "$path" | grep '^/' > /dev/null ; then
  printf '%s\n' "$path"
  exit 0
fi

#Resolve for each occurrence of ".." at the beginning of the given path.
#For performance, don't worry about ".." in the middle of the path.
while true
do
  case "$path" in
    ..*)
      if [ "$cwd" = '/' ]; then
        printf 'Invalid relative path\n' >&2
        exit 1
      fi
      if [ "$path" = '..' ]; then
        path=
      else
        path=`echo "$path" | sed 's;^\.\./;;'`
      fi
      cwd=`dirname $cwd`
      ;;
    *)
      break
      ;;
  esac
done

cwd=`echo "$cwd" | sed 's;/$;;'`
if [ -z "$path" ]; then
  if [ -z "$cwd" ]; then
    cwd='/'
  fi
  printf '%s\n' "$cwd"
else
  printf '%s/%s\n' "$cwd" "$path"
fi

Это решение написано для Bourne Shell для переносимости. У меня есть это в сценарии с именем "respath.sh".

Этот скрипт можно использовать так:

respath.sh '/path/to/file'

Или вот так

respath.sh '/relative/path/here' '/path/to/resolve/relative/to'

Сценарий разрешает путь в первом аргументе, используя путь из второго аргумента в качестве начальной точки. Если указан только первый аргумент, сценарий разрешает путь относительно вашего текущего рабочего каталога.

Sildoreth
источник
Обратите внимание, что вы, вероятно, найдете только оболочку Bourne в Solaris 10 и более ранних версиях. Эта оболочка Bourne, как и большинство реализаций оболочки Bourne начиная с SVR2 (1984), имеет pwdвстроенную функцию и не поддерживает ее -P(оболочка Bourne не реализовала такую ​​логическую обработку $ PWD, как это делают оболочки POSIX).
Стефан Шазелас
Если вы потеряете совместимость с Bourne, то вы можете использовать синтаксис POSIX $ {var ## pattern} и избежать тех эхо | седов, которые могут сломаться с некоторыми значениями.
Стефан Шазелас
Не забудьте проверить состояние выхода cd (и pwd). Вы, вероятно, должны использовать cd -P --также в случае, если 1-й аргумент содержит некоторые ..
Стефан Шазелас
Ваш caseчек должен быть ..|../*)вместо ..*.
Стефан Шазелас
Есть один случай пропущенных цитатdirname $cwd
Стефан Шазелас
0

В случае, если, кроме того, у вас есть некоторый предопределенный путь для $1(обычно ${PWD}), будет работать следующее:

CWD="${1:-${PredefinedAbsolutePath}}"
if [ "${CWD}" != "${PredefinedAbsolutePath}}" ]; then
    case $1 in
        /*) echo "CWD=${CWD}"; ;;
        *) CWD=$(realpath $1); echo "CWD=${CWD}"; ;;
    esac
fi
Нихилу
источник
0

Уважая ответ всех остальных, я обнаружил, что приведенная ниже функция очень проста и удобна для переноса между linux / MAC OSX, чем другие, которые я нашел везде. Может помочь кому-нибудь.

#!/usr/bin/bash
get_absolute_path(){
    file_path=`pwd $1`
    echo "$file_path/$1"
}
#to assign to a variable
absolute_path=$(get_absolute_path "file.txt")
echo $absolute_path
lauksas
источник
-1

Предположим, что переменная a хранит имя пути (a = "what")

1. Для потенциального пространства, содержащего путь:

c=$(grep -o " " <<< $a | wc -l); if (( $c > "0" )); then a=$(sed 's/ /\\ /g' <<< $a); fi

2. Для актуального вопроса, т.е. преобразования пути в абсолютный: readlink может получить содержимое символической ссылки, которое можно использовать следующим образом. (примечание readlink -fбыло бы идеально, но не доступно по крайней мере в Mac OS X bash, в котором -f обозначает ФОРМАТЫ .)

if [[ $(readlink $a) == "" ]]; then a=$(cd $a; pwd); else a=$(cd $(readlink $a); pwd); fi

3. Только что узнал pwd -Pв man pwd: -P это « Показать физический текущий рабочий каталог (все символические ссылки разрешены) » и, следовательно,

if (( $(grep -o " " <<< $a | wc -l) > "0" )); then a=$(sed 's/ /\\ /g' <<< $a); fi; a=$(cd $a; pwd -P)

работать тоже буду.

Вывод: объедините 1 с 2, чтобы удовлетворить оба ваших требования, в то время как имена путей, содержащие пробелы, уже рассмотрены в более кратком 3 .

vxtal
источник
Вы не правы. Например, если ваш путь содержит пробел, большинство из вышеперечисленного не сработает.
Антон
или шариковый символ иногда. И это не разрешает символические ссылки, как было запрошено.
dave_thompson_085
Я удалил свой ответ «комментарий» относительно имен путей, содержащих пробелы, и соответственно изменил часть «ответ».
vxtal