Пример:
absolute="/foo/bar"
current="/foo/baz/foo"
# Magic
relative="../../bar"
Как мне создать магию (надеюсь, не слишком сложный код ...)?
bash
shell
path
relative-path
absolute-path
Пол Тарьян
источник
источник
realpath --relative-to=$absolute $current
.Ответы:
Я думаю, что использование realpath из GNU coreutils 8.23 является самым простым:
Например:
источник
$ realpath --relative-to="${PWD}" "$file"
полезно, если вы хотите пути относительно текущего рабочего каталога./usr/bin/nmap/
-path, но не для/usr/bin/nmap
: отnmap
к/tmp/testing
только../../
и не 3 раза../
. Это работает, однако, потому что делать..
на rootfs есть/
.--relative-to=…
ожидает каталог и не проверяет. Это означает, что вы получите дополнительный «../», если вы запрашиваете путь относительно файла (как, кажется, делает этот пример, потому что/usr/bin
редко или никогда не содержит каталогов иnmap
обычно является двоичным)дает:
источник
relpath(){ python -c "import os.path; print os.path.relpath('$1','${2:-$PWD}')" ; }
python -c 'import os, sys; print(os.path.relpath(*sys.argv[1:]))'
работает максимально естественно и надежно.Это исправленное, полностью функциональное улучшение решения @pini, которое в настоящее время имеет наивысшую оценку (к сожалению, оно работает только в нескольких случаях)
Напоминание: проверка «-z», если строка имеет нулевую длину (= пусто), и проверка «-n», если строка не пустая.
Тестовые случаи:
источник
source=$1; target=$2
наsource=$(realpath $1); target=$(realpath $2)
realpath
рекомендуется, иsource=$(readlink -f $1)
т. Д., Если realpath недоступен (не стандарт)$source
и$target
вот так: `if [[-e $ 1]]; затем source = $ (readlink -f $ 1); иначе источник = 1 $; fi if [[-e $ 2]]; затем target = $ (readlink -f $ 2); иначе цель = 2 $; Таким образом, функция может обрабатывать реальные / существующие относительные пути, а также вымышленные каталоги.readlink
есть-m
опция, которая просто делает это;)источник
Он встроен в Perl с 2001 года, поэтому работает практически на всех системах, которые вы можете себе представить, даже на VMS .
Кроме того, решение легко понять.
Итак, для вашего примера:
... будет работать нормально.
источник
say
не был доступен в Perl для журнала, но он мог бы эффективно использоваться здесь.perl -MFile::Spec -E 'say File::Spec->abs2rel(@ARGV)'
perl -MFile::Spec -e 'print File::Spec->abs2rel(@ARGV)' "$target"
иperl -MFile::Spec -e 'print File::Spec->abs2rel(@ARGV)' "$target" "$origin"
. Первый однострочный perl- скрипт использует один аргумент (origin - текущий рабочий каталог). Второй однострочный Perl- скрипт использует два аргумента.perl
можно найти почти везде, хотя ответ по-прежнему однострочно.Предполагается, что вы установили: bash, pwd, dirname, echo; тогда relpath
Я играл в гольф ответ от Пини и несколько других идей
Примечание . Для этого необходимо, чтобы оба пути были существующими папками. Файлы не будут работать.
источник
Питона
os.path.relpath
как функция оболочкиЦель этого
relpath
упражнения состоит в том, чтобы имитироватьos.path.relpath
функцию Python 2.7 (доступна из Python версии 2.6, но работает должным образом только в 2.7), как предложено xni . Как следствие, некоторые результаты могут отличаться от функций, представленных в других ответах.(Я не тестировал с символами новой строки в путях просто потому, что это нарушает валидацию, основанную на вызовах
python -c
из ZSH. Это, безусловно, было бы возможно с некоторыми усилиями.)Что касается «магии» в Bash, я давно перестал искать магию в Bash, но с тех пор я нашел всю магию, которая мне нужна, а затем и некоторую, в ZSH.
Следовательно, я предлагаю две реализации.
Первая реализация стремится быть полностью POSIX-совместимой . Я проверил это
/bin/dash
на Debian 6.0.6 «Squeeze». Он также отлично работает с/bin/sh
OS X 10.8.3, которая на самом деле является версией Bash 3.2, претендующей на роль оболочки POSIX.Вторая реализация - это функция оболочки ZSH, устойчивая к множественным слешам и другим неприятностям в путях. Если у вас есть доступный ZSH, это рекомендуемая версия, даже если вы вызываете ее в форме скрипта, представленной ниже (то есть с шаблоном
#!/usr/bin/env zsh
) из другой оболочки.Наконец, я написал сценарий ZSH, который проверяет вывод
relpath
команды, найденной в$PATH
заданных тестах, приведенных в других ответах. Я добавил в эти тесты некоторую изюминку, добавив несколько пробелов, табуляций и знаков препинания, таких как! ? *
здесь и там, а также добавил еще один тест с экзотическими символами UTF-8, найденными в vim-powerline .Функция оболочки POSIX
Во-первых, POSIX-совместимая функция оболочки. Он работает с различными путями, но не очищает несколько слэшей и не разрешает символические ссылки.
Функция оболочки ZSH
Теперь более надежная
zsh
версия. Если вы хотите разрешить аргументы в реальных путяхrealpath -f
(доступно вcoreutils
пакете Linux ), замените:a
строки 3 и 4 на:A
.Чтобы использовать это в zsh, удалите первую и последнюю строку и поместите его в каталог, который находится в вашей
$FPATH
переменной.Тестовый скрипт
Наконец, тестовый скрипт. Он принимает одну опцию, а именно
-v
включить подробный вывод.источник
/
Я не боюсь, что первый путь заканчивается .Над сценарием оболочки был вдохновлен Пини (Спасибо!). Это вызывает ошибку в модуле подсветки синтаксиса переполнения стека (по крайней мере, в моем окне предварительного просмотра). Поэтому, пожалуйста, игнорируйте, если подсветка неверна.
Некоторые заметки:
За исключением упомянутых последовательностей обратной косой черты, последняя строка функции «relPath» выводит имена путей, совместимые с python:
Последняя строка может быть заменена (и упрощена) на строку
Я предпочитаю последнее, потому что
Имена файлов могут быть непосредственно добавлены в пути dir, полученные relPath, например:
Символические ссылки в том же каталоге, созданные с помощью этого метода, не имеют отвратительного
"./"
префикса к имени файла.Листинг кода для регрессионных тестов (просто добавьте его в скрипт оболочки):
источник
Не многие ответы здесь являются практичными для повседневного использования. Поскольку это очень трудно сделать правильно в чистом виде, я предлагаю следующее, надежное решение (аналогично одному предложению, скрытому в комментарии):
Затем вы можете получить относительный путь на основе текущего каталога:
или вы можете указать, что путь должен быть относительно данного каталога:
Недостатком является то, что это требует Python, но:
Обратите внимание, что решения, которые включают
basename
илиdirname
могут не обязательно быть лучше, так как требуютcoreutils
их установки. Если у кого-то есть чистоеbash
решение, которое является надежным и простым (а не извилистым любопытством), я был бы удивлен.источник
Этот скрипт дает правильные результаты только для входных данных, которые являются абсолютными или относительными путями без
.
или..
:источник
Я бы просто использовал Perl для этой не очень тривиальной задачи:
источник
perl -MFile::Spec -e "print File::Spec->abs2rel('$absolute','$current')"
так что абсолютные и текущие котируются.relative=$(perl -MFile::Spec -e 'print File::Spec->abs2rel(@ARGV)' "$absolute" "$current")
. Это гарантирует, что значения сами по себе не могут содержать код perl!Небольшое улучшение ответов Каску и Пини , которое лучше подходит для пробелов и позволяет проходить относительные пути:
источник
test.sh:
Тестирование:
источник
readlink
на$(pwd)
.Еще одно решение, pure
bash
+ GNUreadlink
для легкого использования в следующем контексте:Это работает практически во всех современных Linux. Если
readlink -m
не работает на вашей стороне, попробуйтеreadlink -f
вместо этого. Смотрите также https://gist.github.com/hilbix/1ec361d00a8178ae8ea0 для возможных обновлений:Ноты:
*
или?
.ln -s
:relpath / /
дает.
а не пустую строкуrelpath a a
даетa
, даже еслиa
случается, что каталогreadlink
требуется для канонизации путей.readlink -m
этому работает и для еще не существующих путей.В старых системах, где
readlink -m
недоступно, происходитreadlink -f
сбой, если файл не существует. Таким образом, вам, вероятно, нужен обходной путь, подобный этому (непроверенный!):Это не совсем правильно в случае
$1
включенных.
или..
отсутствующих путей (например, в/doesnotexist/./a
), но это должно охватывать большинство случаев.(Заменить
readlink -m --
выше наreadlink_missing
.)Редактировать из-за downvote следует
Вот проверка того, что эта функция действительно верна:
Озадаченный? Ну, это правильные результаты ! Даже если вы считаете, что вопрос не подходит, вот доказательство того, что это правильно:
Без сомнения,
../bar
это точный и единственный правильный относительный путь страницы,bar
видимой со страницыmoo
. Все остальное было бы просто неправильно.Тривиально принять вывод к вопросу, который, очевидно, предполагает, что
current
это каталог:Это возвращает именно то, о чем просили.
И прежде чем поднять бровь, вот немного более сложный вариант
relpath
(обратите внимание на небольшую разницу), который также должен работать и для URL-синтаксиса (так что трейлинг/
выживает, благодаря некоторомуbash
-magic):И вот проверки, чтобы прояснить: это действительно работает как сказано.
И вот как это можно использовать для получения желаемого результата из вопроса:
Если вы найдете что-то, что не работает, пожалуйста, дайте мне знать в комментариях ниже. Спасибо.
PS:
Почему аргументы
relpath
«перевернуты» в отличие от всех остальных ответов здесь?Если вы измените
в
затем вы можете оставить второй параметр, так что BASE - это текущий каталог / URL / что угодно. Это только принцип Unix, как обычно.
Если вам это не нравится, пожалуйста, вернитесь в Windows. Спасибо.
источник
К сожалению, ответ Марка Рушакова (теперь удаленный - он ссылался на код отсюда ), кажется, не работает правильно, когда адаптирован к:
Мышление, изложенное в комментарии, может быть улучшено, чтобы оно работало правильно в большинстве случаев. Я собираюсь предположить, что скрипт принимает исходный аргумент (где вы находитесь) и целевой аргумент (где вы хотите попасть), и что либо оба являются абсолютными путями, либо оба являются относительными. Если одно является абсолютным, а другое относительным, проще всего сделать префикс относительного имени с текущим рабочим каталогом - но приведенный ниже код этого не делает.
берегись
Код ниже близок к правильной работе, но не совсем правильно.
xyz/./pqr
».xyz/../pqr
»../
' из путей.Код Денниса лучше, потому что он исправляет 1 и 5 - но имеет те же проблемы 2, 3, 4. Из-за этого используйте код Денниса (и проголосуйте заранее).
(Примечание: POSIX предоставляет системный вызов,
realpath()
который разрешает имена путей, так что в них не остается символических ссылок. Применение этого к входным именам, а затем использование кода Денниса даст правильный ответ каждый раз. Это тривиально, чтобы написать код C, который обертыванияrealpath()
- я сделал это - но я не знаю стандартной утилиты, которая делает это.)Для этого я считаю, что Perl проще в использовании, чем shell, хотя bash имеет приличную поддержку массивов и, вероятно, может сделать это тоже - упражнение для читателя. Итак, учитывая два совместимых имени, разбейте их каждое на компоненты:
Таким образом:
Тестовый скрипт (квадратные скобки содержат пробел и вкладку):
Вывод из тестового скрипта:
Этот сценарий Perl довольно тщательно работает на Unix (он не учитывает все сложности имен путей Windows), несмотря на странные вводные данные. Он использует модуль
Cwd
и его функциюrealpath
для определения реального пути имен, которые существуют, и выполняет текстовый анализ для путей, которые не существуют. Во всех случаях, кроме одного, он выдает тот же результат, что и сценарий Денниса. Девиантный случай:Два результата эквивалентны - просто не идентичны. (Вывод сделан из слегка измененной версии тестового сценария - приведенный ниже сценарий Perl просто печатает ответ, а не входные данные и ответ, как в приведенном выше сценарии.) Теперь: я должен исключить нерабочий ответ? Может быть...
источник
/home/part1/part2
к/
имеет слишком много../
. В противном случае мой сценарий совпадает с вашим выводом, за исключением того, что мой добавляет ненужный.
в конце того, где находится пункт назначения,.
и я не использую./
в начале те, которые спускаются, не повышаясь.readlink /usr/bin/vi
дает/etc/alternatives/vi
, но это еще одна символическая ссылка - в то время какreadlink -f /usr/bin/vi
дает/usr/bin/vim.basic
, который является конечным пунктом назначения всех символических ссылок ...Я принял ваш вопрос как задачу написать это в «переносимом» коде оболочки, т.е.
Он работает на любой POSIX-совместимой оболочке (zsh, bash, ksh, ash, busybox, ...). Он даже содержит тестовый набор для проверки его работы. Канонизация имен путей оставлена в качестве упражнения. :-)
источник
Мое решение:
источник
Вот моя версия. Он основан на ответ по @Offirmo . Я сделал его Dash-совместимым и исправил следующую ошибку тестового набора:
./compute-relative.sh "/a/b/c/de/f/g" "/a/b/c/def/g/"
->"../..f/g/"
Сейчас:
CT_FindRelativePath "/a/b/c/de/f/g" "/a/b/c/def/g/"
->"../../../def/g/"
Смотрите код:
источник
Угадай, этот тоже сделает свое дело ... (поставляется со встроенными тестами) :)
ОК, ожидаются некоторые накладные расходы, но мы здесь работаем над оболочкой Bourne! ;)
источник
Этот скрипт работает только с путями. Для этого не требуется никаких файлов. Если пройденные пути не являются абсолютными, поведение будет немного необычным, но оно должно работать так, как ожидается, если оба пути являются относительными.
Я только протестировал его на OS X, поэтому он не может быть переносимым.
источник
Этот ответ не касается Bash-части вопроса, а потому что я пытался использовать ответы в этом вопросе для реализации этой функциональности в Emacs я добавлю его туда.
Emacs на самом деле имеет функцию для этого из коробки:
источник
relpath
функция), ведет себя идентичноfile-relative-name
для предоставленных вами тестов.Вот скрипт оболочки, который делает это без вызова других программ:
источник: http://www.ynform.org/w/Pub/Relpath
источник
Мне нужно было что-то подобное, но также разрешало символические ссылки. Я обнаружил, что для этой цели у pwd есть флаг -P. Фрагмент моего сценария прилагается. Это внутри функции в сценарии оболочки, следовательно, $ 1 и $ 2. Значение результата, которое является относительным путем от START_ABS до END_ABS, находится в переменной UPDIRS. Компакт-диск скрипта входит в каждый каталог параметров, чтобы выполнить pwd -P, и это также означает, что параметры относительного пути обрабатываются. Ура, джим
источник