Это напечатает путь дословно, если он начинается с /. Если нет, это должен быть относительный путь, поэтому он $PWDначинается впереди. #./Часть отсекает ./от передней части $1.
Я также заметил функцию C, но не смог найти соответствующий двоичный файл или что-то еще. В любом случае, ваша функция отлично подходит для моих нужд. Спасибо!
The Mighty Rubber Duck
7
Обратите внимание, что это не разыменовывает символические ссылки.
У меня это сработало, но я переименовал его в «realpath_osx ()», так как мне МОЖЕТ потребоваться отправить этот сценарий bash в оболочку Linux, и я не хочу, чтобы он конфликтовал с «настоящим» реальным путем! (Я уверен, что есть более элегантный способ, но я bash n00b.)
Это решает проблему, но требует установки того, что вам может не понадобиться или которое может не быть в целевой системе, например OS X
Джейсон С.
7
@JasonS GNU Coreutils слишком хорош, чтобы не использовать. Он включает в себя множество замечательных утилит. На самом деле это так хорошо, что ядро Linux бесполезно без него, и почему некоторые люди называют его GNU / Linux. Coreutils великолепен. отличный ответ это должен быть выбранный ответ.
7
@omouse В вопросе конкретно упоминается «нестандартное решение» (для OS X). Независимо от того, насколько хорош coreutils, он не является «из коробки» в OS X, так что ответ плохой. Вопрос не в том, насколько хорош coreutils или лучше ли он, чем то, что есть в OS X. Есть готовые решения, которые $( cd "$(dirname "$0")" ; pwd -P )мне подходят .
Jason S
2
Одна из «особенностей» OS X заключается в том, что каталог и имена файлов не чувствительны к регистру. В результате, если у вас есть каталог, который называется, XXXи кто- cd xxxто pwdвернется .../xxx. За исключением этого ответа, все вышеперечисленные решения возвращаются xxxтогда, когда вы действительно хотите XXX. Спасибо!
Эндрю
1
Я не знаю, чем бесконечное копирование, вставка и переосмысление кода лучше, чем существующее решение для диспетчера пакетов. Если вам нужно realpath, то что произойдет, если вам почти наверняка понадобятся другие предметы coreutils? Переписать и эти функции в bash? : P
Иезекииль Виктор
28
Тьфу. Я обнаружил, что предыдущие ответы немного не подходят по нескольким причинам: в частности, они не разрешают несколько уровней символических ссылок, и они чрезвычайно "Bash-y". Хотя в исходном вопросе явно содержится запрос на «сценарий Bash», в нем также упоминается BSD-подобный Mac OS X, отличный от GNU readlink. Итак, вот попытка некоторой разумной переносимости (я проверил это с помощью bash как 'sh' и тире), разрешая произвольное количество символических ссылок; и он также должен работать с пробелами в пути (ах), хотя я не уверен в поведении, если есть пробелы в базовом имени самой утилиты, так что, может быть, гм, избежать этого?
#!/bin/sh
realpath(){
OURPWD=$PWD
cd "$(dirname "$1")"
LINK=$(readlink "$(basename "$1")")while["$LINK"];do
cd "$(dirname "$LINK")"
LINK=$(readlink "$(basename "$1")")done
REALPATH="$PWD/$(basename "$1")"
cd "$OURPWD"
echo "$REALPATH"}
realpath "$@"
Я бы рекомендовал использовать только localпеременные, определенные внутри функции, чтобы не загрязнять глобальное пространство имен. Напр local OURPWD=.... Работает по крайней мере для bash.
Майкл Пэсолд
Кроме того, в коде не следует использовать заглавные буквы для частных переменных. Переменные в верхнем регистре зарезервированы для использования системой.
tripleee
Спасибо за сценарий. В случае, если ссылка (и) и реальный файл имеют разные базовые имена, вероятно, было бы неплохо добавить BASENAME=$(basename "$LINK") внутри while и использовать его во втором установщике LINK и установщике REALPATH
stroborobo
Это не обрабатывает символические ..ссылки и родительские ссылки, как это realpathбыло бы. С доморощенным coreutilsустановлено, попробуйте ln -s /var/log /tmp/linkexampleтогда realpath /tmp/linkexample/../; это печатает /private/var. Но /tmp/linkexample/..вместо этого ваша функция производит , потому что ..это не символическая ссылка.
Мартейн Питерс
10
Вариант решения Python, более удобный для командной строки:
На всякий случай, если кто-то достаточно сумасшедший, чтобы запустить интерпретатор Python для одной команды…
Бахзау
Я был достаточно зол, но использовалpython -c "import os; import sys; print(os.path.realpath(sys.argv[1]))"
Алекс Чемберлен
7
Я искал решение для использования в сценарии подготовки системы, то есть запускать его до того, как Homebrew будет установлен. Не имея подходящего решения, я бы просто переложил задачу на кроссплатформенный язык, например Perl:
Большой! Perl хорош из-за небольших накладных расходов! Вероятно, вы могли бы упростить первую версию до FULLPATH=$(perl -e "use Cwd 'abs_path'; print abs_path('$0')"). Есть основания против?
F Pereira
@FPereira Создание программного кода из неэкранированной строки, введенной пользователем, никогда не является хорошей идеей. ''не пуленепробиваемый. $0Например, он ломается, если содержит одинарную кавычку. Очень простой пример: попробуйте свою версию /tmp/'/test.shи вызовите /tmp/'/test.shее по полному пути.
4ae1e1
Или еще проще, /tmp/'.sh.
4ae1e1
6
Поскольку существует реальный путь, как указывали другие:
мы могли бы просто сделать gcc realpath.c -o /usr/local/bin/realpath?
Alexander Mills
1
@AlexanderMills Вы не должны запускать компилятор от имени пользователя root; а затем , если вы этого не сделаете, вы не будете иметь права на запись/usr/local/bin
tripleee
6
Используйте Python, чтобы получить это:
#!/usr/bin/env python
import os
import sys
print(os.path.realpath(sys.argv[1]))
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, почему и / или как этот код отвечает на вопрос, улучшает его долгосрочную ценность.
Игорь Ф.
1
Как вы можете видеть выше, я попробовал это около 6 месяцев назад. Я совершенно забыл об этом, пока не обнаружил, что снова нуждаюсь в подобном. Я был
полностью шокирован увидев, насколько это было примитивно; Я уже около года довольно интенсивно учу себя программировать, но мне часто кажется, что я вообще ничего не научился, когда дела идут хуже всего.
Я бы удалил указанное выше «решение», но мне действительно нравится, что оно является своего рода записью того, сколько я действительно узнал за последние несколько месяцев.
Но я отвлекся. Я сел и разобрался со всем этим вчера вечером. Объяснения в комментариях должно быть достаточно. Если вы хотите отследить копию, над которой я продолжаю работать, вы можете следовать этой сути. Вероятно, это то, что вам нужно.
#!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively## dereference symbolic links (ala 'readlink') until the originating file## is found. This is effectively the same function provided in stdlib.h as## 'realpath' and on the command line in GNU 'readlink -f'.## Neither of these tools, however, are particularly accessible on the many## systems that do not have the GNU implementation of readlink, nor ship## with a system compiler (not to mention the requisite knowledge of C).## This script is written with portability and (to the extent possible, speed)## in mind, hence the use of printf for echo and case statements where they## can be substituded for test, though I've had to scale back a bit on that.## It is (to the best of my knowledge) written in standard POSIX shell, and## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have## issues with it, though I'm not sure why; so probably best to avoid for now.## Particularly useful (in fact, the reason I wrote this) is the fact that## it can be used within a shell script to find the path of the script itself.## (I am sure the shell knows this already; but most likely for the sake of## security it is not made readily available. The implementation of "$0"## specificies that the $0 must be the location of **last** symbolic link in## a chain, or wherever it resides in the path.) This can be used for some## ...interesting things, like self-duplicating and self-modifiying scripts.## Currently supported are three errors: whether the file specified exists## (ala ENOENT), whether its target exists/is accessible; and the special## case of when a sybolic link references itself "foo -> foo": a common error## for beginners, since 'ln' does not produce an error if the order of link## and target are reversed on the command line. (See POSIX signal ELOOP.)## It would probably be rather simple to write to use this as a basis for## a pure shell implementation of the 'symlinks' util included with Linux.## As an aside, the amount of code below **completely** belies the amount## effort it took to get this right -- but I guess that's coding for you.##===-------------------------------------------------------------------===##for argv;do:;done# Last parameter on command line, for options parsing.## Error messages. Use functions so that we can sub in when the error occurs.
recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n";}
dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n";}
errnoent(){ printf "No such file: "$@"\n";}# Borrow a horrible signal name.# Probably best not to install as 'pathfull', if you can avoid it.
pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")"## 'test and 'ls' report different status for bad symlinks, so we use this.if[!-e "$@"];thenif $(ls -d "$@"2>/dev/null)2>/dev/null;then
errnoent 1>&2; exit 1;elif[!-e "$@"-a "$link"="$@"];then
recurses 1>&2; exit 1;elif[!-e "$@"]&&[!-z "$link"];then
dangling 1>&2; exit 1;fifi## Not a link, but there might be one in the path, so 'cd' and 'pwd'.if[-z "$link"];thenif["$(dirname "$@" | cut -c1)"='/'];then
printf "$@\n"; exit 0;else printf "$(pwd)/$(basename "$@")\n";fi; exit 0fi## Walk the symlinks back to the origin. Calls itself recursivly as needed.while["$link"];do
cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"case"$newlink"in"$link") dangling 1>&2&& exit 1;;'') printf "$(pwd)/$(basename "$link")\n"; exit 0;;*) link="$newlink"&& pathfull "$link";;esacdone
printf "$(pwd)/$(basename "$newlink")\n"}## Demo. Install somewhere deep in the filesystem, then symlink somewhere ## else, symlink again (maybe with a different name) elsewhere, and link## back into the directory you started in (or something.) The absolute path## of the script will always be reported in the usage, along with "$0".if[-z "$argv"];then scriptname="$(pathfull "$0")"# Yay ANSI l33t codes! Fancy.
printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m "
printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n "
printf "Recursive readlink for the authoritative file, symlink after "
printf "symlink.\n\n\n \033[4m$scriptname\033[24m\n\n "
printf " From within an invocation of a script, locate the script's "
printf "own file\n (no matter where it has been linked or "
printf "from where it is being called).\n\n"else pathfull "$@"fi
Обратите внимание, что ваша реализация не разрешает символические ..ссылки перед родительскими ссылками; например /foo/link_to_other_directory/.., разрешается как /fooродительский элемент любого пути, на который /foo/link_to_other_directoryуказывает символическая ссылка . readlink -fи realpathразрешить каждый компонент пути, начиная с корня и обновляя добавляемые целевые объекты ссылки до остальных, которые все еще обрабатываются. Я добавил ответ на этот вопрос, заново реализуя эту логику.
На самом деле это не разрешает символические ссылки так, как это сделал бы realpath. Он разрешает ..родительские ссылки до разрешения символических ссылок, а не после; попробуйте это с coreutilsустановленным homebrew , создайте ссылку, ln -s /var/log /tmp/linkexampleзатем запустите realpath /tmp/linkexample/../; это печатает /private/var. Но /tmp/linkexample/..вместо этого ваша функция производит , потому что pwdвсе еще отображается /tmp/linkexampleпосле компакт-диска.
Мартейн Питерс
1
В macOS я нашел единственное решение, которое надежно обрабатывает символические ссылки - это использование realpath. Поскольку для этого требуется brew install coreutils, я просто автоматизировал этот шаг. Моя реализация выглядит так:
#!/usr/bin/env bashset-e
if! which realpath >&/dev/null;thenif! which brew >&/dev/null;then
msg="ERROR: This script requires brew. See https://brew.sh for installation instructions."
echo "$(tput setaf 1)$msg$(tput sgr0)">&2
exit 1fi
echo "Installing coreutils/realpath"
brew install coreutils >&/dev/null
fi
thisDir=$( dirname "`realpath "$0"`")
echo "This script is run from \"$thisDir\""
Это ошибки, если их нет brew установлены, но вы также можете просто установить это. Мне просто неудобно автоматизировать что-то, что извлекает произвольный код Ruby из сети.
Обратите внимание, что это автоматизированный вариант ответа Олега Михеева .
Один важный тест
Хороший тест любого из этих решений:
поместите код в файл сценария где-нибудь
в другом каталоге, символическая ссылка ( ln -s) на этот файл
запустить скрипт из этой символической ссылки
Разыменовывает ли решение символическую ссылку и дает ли вам исходный каталог? Если да, то это работает.
Мне нужна была realpathзамена OS X, которая правильно работает с путями с символическими ссылками и родительскими ссылками, как и если readlink -fбы . Это включает разрешение символических ссылок в пути перед разрешением родительских ссылок; например, если вы установили coreutilsбутылку для домашнего пивоварения , запустите:
$ ln -s /var/log/cups /tmp/linkeddir # symlink to another directory
$ greadlink -f /tmp/linkeddir/..# canonical path of the link parent/private/var/log
Обратите внимание, что readlink -fэто разрешено /tmp/linkeddirдо разрешения ..ссылки на родительский каталог. Конечно, readlink -fна Mac тоже нет .
# shellcheck shell=bashset-euo pipefail
_contains(){# return true if first argument is present in the other argumentslocal elem value
value="$1"
shift
for elem in"$@";doif[[ $elem =="$value"]];thenreturn0fidonereturn1}
_canonicalize_filename_mode(){# resolve any symlink targets, GNU readlink -f style# where every path component except the last should exist and is# resolved if it is a symlink. This is essentially a re-implementation# of canonicalize_filename_mode(path, CAN_ALL_BUT_LAST).# takes the path to canonicalize as first argumentlocal path result component seen
seen=()
path="$1"
result="/"if[[ $path !=/*]];then# add in current working dir if relative
result="$PWD"fiwhile[[-n $path ]];do
component="${path%%/*}"case"$component"in'')# empty because it started with /
path="${path:1}";;.)# ./ current directory, do nothing
path="${path:1}";;..)# ../ parent directoryif[[ $result !="/"]];then# not at the root?
result="${result%/*}"# then remove one element from the pathfi
path="${path:2}";;*)# add this component to the result, remove from pathif[[ $result !=*/]];then
result="$result/"fi
result="$result$component"
path="${path:${#component}}"# element must exist, unless this is the final componentif[[ $path =~[^/]&&!-e $result ]];then
echo "$1: No such file or directory">&2return1fi# if the result is a link, prefix it to the path, to continue resolvingif[[-L $result ]];thenif _contains "$result""${seen[@]+"${seen[@]}"}";then# we've seen this link before, abort
echo "$1: Too many levels of symbolic links">&2return1fi
seen+=("$result")
path="$(readlink "$result")$path"if[[ $path =/*]];then# if the link is absolute, restart the result from /
result="/"elif[[ $result !="/"]];then# otherwise remove the basename of the link from the result
result="${result%/*}"fielif[[ $path =~[^/]&&!-d $result ]];then# otherwise all but the last element must be a dir
echo "$1: Not a directory">&2return1fi;;esacdone
echo "$result"}
Он включает в себя обнаружение циклических символических ссылок, выходящих, если один и тот же (промежуточный) путь встречается дважды.
Если все, что вам нужно readlink -f, это можно использовать как:
readlink(){if[[ $1 !=-f ]];then# poor-man's option parsing# delegate to the standard readlink command
command readlink "$@"returnfilocal path result seenerr
shift
seenerr=for path in"$@";do# by default readlink suppresses error messagesif! result=$(_canonicalize_filename_mode "$path"2>/dev/null);then
seenerr=1continuefi
echo "$result"doneif[[ $seenerr ]];thenreturn1;fi}
Для realpath, я также нужен --relative-toи --relative-baseподдержки, которые дают вам относительные пути после канонизации:
_realpath(){# GNU realpath replacement for bash 3.2 (OS X)# accepts --relative-to= and --relative-base options# and produces canonical (relative or absolute) paths for each# argument on stdout, errors on stderr, and returns 0 on success# and 1 if at least 1 path triggered an error.local relative_to relative_base seenerr path
relative_to=
relative_base=
seenerr=while[[ $# -gt 0 ]]; docase $1 in"--relative-to="*)
relative_to=$(_canonicalize_filename_mode "${1#*=}")
shift 1;;"--relative-base="*)
relative_base=$(_canonicalize_filename_mode "${1#*=}")
shift 1;;*)break;;esacdoneif[[-n $relative_to
&&-n $relative_base
&& ${relative_to#${relative_base}/} == "$relative_to"]];then# relative_to is not a subdir of relative_base -> ignore both
relative_to=
relative_base=elif[[-z $relative_to &&-n $relative_base ]];then# if relative_to has not been set but relative_base has, then# set relative_to from relative_base, simplifies logic later on
relative_to="$relative_base"fifor path in"$@";doif! real=$(_canonicalize_filename_mode "$path");then
seenerr=1continuefi# make path relative if so requiredif[[-n $relative_to
&&(# path must not be outside relative_base to be made relative-z $relative_base || ${real#${relative_base}/} != "$real")]];thenlocal common_part parentrefs
common_part="$relative_to"
parentrefs=while[[ ${real#${common_part}/} == "$real" ]]; do
common_part="$(dirname "$common_part")"
parentrefs="..${parentrefs:+/$parentrefs}"doneif[[ $common_part !="/"]];then
real="${parentrefs:+${parentrefs}/}${real#${common_part}/}"fifi
echo "$real"doneif[[ $seenerr ]];thenreturn1fi}if! command -v realpath >/dev/null 2>&1;then# realpath is not available on OSX unless you install the `coreutils` brew
realpath(){ _realpath "$@";}fi
Основываясь на общении с комментатором, я согласился, что это очень сложно, и у него нет альтернативного способа реализации реального пути, который ведет себя полностью так же, как Ubuntu.
Но следующая версия может обрабатывать угловые случаи, лучший ответ не может и удовлетворить мои повседневные потребности на MacBook. Поместите этот код в свой ~ / .bashrc и запомните:
arg может быть только 1 файлом или dir, без подстановочного знака
без пробелов в каталоге или имени файла
по крайней мере, существует родительский каталог файла или каталога
не стесняйтесь использовать. .. / вещь, это безопасно
# 1. if is a dir, try cd and pwd# 2. if is a file, try cd its parent and concat dir+file
realpath(){["$1"=""]&&return1
dir=`dirname "$1"`
file=`basename "$1"`
last=`pwd`[-d "$dir"]&& cd $dir ||return1if[-d "$file"];then# case 1
cd $file && pwd ||return1else# case 2
echo `pwd`/$file | sed 's/\/\//\//g'fi
cd $last
}
Вы хотите избежать бесполезного использованияecho . Просто pwdделает то же самое, echo $(pwd)не имея на нерест второй копии оболочки. Кроме того, отсутствие цитирования аргумента echoявляется ошибкой (вы потеряете все начальные или конечные пробелы, любые смежные внутренние символы пробелов, расширенные подстановочные знаки и т. Д.). См. Далее stackoverflow.com/questions/10067266/…
tripleee
Кроме того, поведение для несуществующих путей ошибочно; но я думаю, это то, что, возможно, пытается сказать фраза «но помни». Хотя поведение в Ubuntu определенно не в том, чтобы печатать текущий каталог, когда вы запрашиваете realpathнесуществующий каталог.
tripleee
Для единообразия, вероятно, предпочтительнее dir=$(dirname "$1"); file=$(basename "$1")использовать давно устаревший синтаксис обратной кавычки. Также обратите внимание на правильное цитирование аргументов.
tripleee
Ваш обновленный ответ, похоже, не исправляет многие ошибки и добавляет новые.
tripleee
пожалуйста, назовите мне несколько конкретных неудачных случаев, потому что все тесты, которые я выполняю на рабочем столе ubuntu 18.04, в порядке, спасибо.
$( cd "$(dirname "$0")" ; pwd -P )
Ответы:
Есть
realpath()
функция C, которая выполнит эту работу, но я не вижу ничего доступного в командной строке. Вот быстрая и грязная замена:Это напечатает путь дословно, если он начинается с
/
. Если нет, это должен быть относительный путь, поэтому он$PWD
начинается впереди.#./
Часть отсекает./
от передней части$1
.источник
realpath ../something
возвращается$PWD/../something
command -v realpath >/dev/null 2>&1 || realpath() { ... }
Эти три простых шага решат эту и многие другие проблемы OS X:
brew install coreutils
grealpath .
(3) можно заменить на просто
realpath
, см. (2) выводисточник
$( cd "$(dirname "$0")" ; pwd -P )
мне подходят .XXX
и кто-cd xxx
тоpwd
вернется.../xxx
. За исключением этого ответа, все вышеперечисленные решения возвращаютсяxxx
тогда, когда вы действительно хотитеXXX
. Спасибо!realpath
, то что произойдет, если вам почти наверняка понадобятся другие предметыcoreutils
? Переписать и эти функции в bash? : PТьфу. Я обнаружил, что предыдущие ответы немного не подходят по нескольким причинам: в частности, они не разрешают несколько уровней символических ссылок, и они чрезвычайно "Bash-y". Хотя в исходном вопросе явно содержится запрос на «сценарий Bash», в нем также упоминается BSD-подобный Mac OS X, отличный от GNU
readlink
. Итак, вот попытка некоторой разумной переносимости (я проверил это с помощью bash как 'sh' и тире), разрешая произвольное количество символических ссылок; и он также должен работать с пробелами в пути (ах), хотя я не уверен в поведении, если есть пробелы в базовом имени самой утилиты, так что, может быть, гм, избежать этого?Надеюсь, что это может быть кому-то полезно.
источник
local
переменные, определенные внутри функции, чтобы не загрязнять глобальное пространство имен. Напрlocal OURPWD=...
. Работает по крайней мере для bash.BASENAME=$(basename "$LINK")
внутри while и использовать его во втором установщике LINK и установщике REALPATH..
ссылки и родительские ссылки, как этоrealpath
было бы. С доморощеннымcoreutils
установлено, попробуйтеln -s /var/log /tmp/linkexample
тогдаrealpath /tmp/linkexample/../
; это печатает/private/var
. Но/tmp/linkexample/..
вместо этого ваша функция производит , потому что..
это не символическая ссылка.Вариант решения Python, более удобный для командной строки:
источник
python -c "import os; import sys; print(os.path.realpath(sys.argv[1]))"
Я искал решение для использования в сценарии подготовки системы, то есть запускать его до того, как Homebrew будет установлен. Не имея подходящего решения, я бы просто переложил задачу на кроссплатформенный язык, например Perl:
Чаще всего нам нужен содержащий каталог:
источник
FULLPATH=$(perl -e "use Cwd 'abs_path'; print abs_path('$0')")
. Есть основания против?''
не пуленепробиваемый.$0
Например, он ломается, если содержит одинарную кавычку. Очень простой пример: попробуйте свою версию/tmp/'/test.sh
и вызовите/tmp/'/test.sh
ее по полному пути./tmp/'.sh
.Поскольку существует реальный путь, как указывали другие:
Makefile:
Затем скомпилируйте
make
и вставьте мягкую ссылку с помощью:ln -s $(pwd)/realpath /usr/local/bin/realpath
источник
gcc realpath.c -o /usr/local/bin/realpath
?/usr/local/bin
Используйте Python, чтобы получить это:
источник
dirname
даст имя каталога/path/to/file
, то есть/path/to
.cd /path/to; pwd
гарантирует, что путь является абсолютным.basename
даст только имя файла в/path/to/file
, то естьfile
.источник
Как вы можете видеть выше, я попробовал это около 6 месяцев назад. Я совершенно забыл об этом, пока не обнаружил, что снова нуждаюсь в подобном. Я был полностью шокирован увидев, насколько это было примитивно; Я уже около года довольно интенсивно учу себя программировать, но мне часто кажется, что я вообще ничего не научился, когда дела идут хуже всего.
Я бы удалил указанное выше «решение», но мне действительно нравится, что оно является своего рода записью того, сколько я действительно узнал за последние несколько месяцев.
Но я отвлекся. Я сел и разобрался со всем этим вчера вечером. Объяснения в комментариях должно быть достаточно. Если вы хотите отследить копию, над которой я продолжаю работать, вы можете следовать этой сути. Вероятно, это то, что вам нужно.
источник
..
ссылки перед родительскими ссылками; например/foo/link_to_other_directory/..
, разрешается как/foo
родительский элемент любого пути, на который/foo/link_to_other_directory
указывает символическая ссылка .readlink -f
иrealpath
разрешить каждый компонент пути, начиная с корня и обновляя добавляемые целевые объекты ссылки до остальных, которые все еще обрабатываются. Я добавил ответ на этот вопрос, заново реализуя эту логику.realpath для Mac OS X
Пример со связанным путем:
Пример с домашней папкой
источник
..
if test -d $path ; then echo $(cd "$path"; pwd) ; else [...]
"$(dirname $(dirname $(realpath $0)))"
как работает, поэтому нужно что-то еще ...echo
также не должно быть совершенно...
родительские ссылки до разрешения символических ссылок, а не после; попробуйте это сcoreutils
установленным homebrew , создайте ссылку,ln -s /var/log /tmp/linkexample
затем запуститеrealpath /tmp/linkexample/../
; это печатает/private/var
. Но/tmp/linkexample/..
вместо этого ваша функция производит , потому чтоpwd
все еще отображается/tmp/linkexample
после компакт-диска.В macOS я нашел единственное решение, которое надежно обрабатывает символические ссылки - это использование
realpath
. Поскольку для этого требуетсяbrew install coreutils
, я просто автоматизировал этот шаг. Моя реализация выглядит так:Это ошибки, если их нет
brew
установлены, но вы также можете просто установить это. Мне просто неудобно автоматизировать что-то, что извлекает произвольный код Ruby из сети.Обратите внимание, что это автоматизированный вариант ответа Олега Михеева .
Один важный тест
Хороший тест любого из этих решений:
ln -s
) на этот файлРазыменовывает ли решение символическую ссылку и дает ли вам исходный каталог? Если да, то это работает.
источник
Кажется, это работает для OSX, не требует никаких двоичных файлов и было извлечено отсюда
источник
Мне это нравится:
источник
Мне нужна была
realpath
замена OS X, которая правильно работает с путями с символическими ссылками и родительскими ссылками, как и еслиreadlink -f
бы . Это включает разрешение символических ссылок в пути перед разрешением родительских ссылок; например, если вы установилиcoreutils
бутылку для домашнего пивоварения , запустите:Обратите внимание, что
readlink -f
это разрешено/tmp/linkeddir
до разрешения..
ссылки на родительский каталог. Конечно,readlink -f
на Mac тоже нет .Итак, как часть реализации bash,
realpath
я повторно реализовал то, что делаетcanonicalize_filename_mode(path, CAN_ALL_BUT_LAST)
вызов функции GNUlib , в Bash 3.2; это также вызов функции, которыйreadlink -f
делает GNU :Он включает в себя обнаружение циклических символических ссылок, выходящих, если один и тот же (промежуточный) путь встречается дважды.
Если все, что вам нужно
readlink -f
, это можно использовать как:Для
realpath
, я также нужен--relative-to
и--relative-base
поддержки, которые дают вам относительные пути после канонизации:Я включил модульные тесты в свой запрос на проверку кода для этого кода .
источник
Основываясь на общении с комментатором, я согласился, что это очень сложно, и у него нет альтернативного способа реализации реального пути, который ведет себя полностью так же, как Ubuntu.
Но следующая версия может обрабатывать угловые случаи, лучший ответ не может и удовлетворить мои повседневные потребности на MacBook. Поместите этот код в свой ~ / .bashrc и запомните:
источник
echo
. Простоpwd
делает то же самое,echo $(pwd)
не имея на нерест второй копии оболочки. Кроме того, отсутствие цитирования аргументаecho
является ошибкой (вы потеряете все начальные или конечные пробелы, любые смежные внутренние символы пробелов, расширенные подстановочные знаки и т. Д.). См. Далее stackoverflow.com/questions/10067266/…realpath
несуществующий каталог.dir=$(dirname "$1"); file=$(basename "$1")
использовать давно устаревший синтаксис обратной кавычки. Также обратите внимание на правильное цитирование аргументов.