Przykład:
absolute="/foo/bar"
current="/foo/baz/foo"
# Magic
relative="../../bar"
Jak stworzyć magię (mam nadzieję, że niezbyt skomplikowany kod ...)?
bash
shell
path
relative-path
absolute-path
Paul Tarjan
źródło
źródło
realpath --relative-to=$absolute $current
.Odpowiedzi:
Myślę, że użycie realpath z GNU coreutils 8.23 jest najprostsze:
Na przykład:
źródło
$ realpath --relative-to="${PWD}" "$file"
przydaje się, jeśli chcesz ścieżki względem bieżącego katalogu roboczego./usr/bin/nmap/
ścieżki, ale nie dla/usr/bin/nmap
: odnmap
do/tmp/testing
jest tylko,../../
a nie 3 razy../
. Działa jednak, ponieważ działa..
na rootfach/
.--relative-to=…
oczekuje katalogu i NIE sprawdza. Oznacza to, że otrzymujesz dodatkowe „../”, jeśli poprosisz o ścieżkę względem pliku (jak wydaje się w tym przykładzie, ponieważ/usr/bin
rzadko lub nigdy nie zawiera katalogów inmap
zwykle jestdaje:
źródło
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:]))'
działa najbardziej naturalnie i niezawodnie.Jest to poprawione, w pełni funkcjonalne ulepszenie obecnie najlepiej ocenianego rozwiązania @pini (które niestety obsługuje tylko kilka przypadków)
Przypomnienie: test „-z”, jeśli łańcuch ma zerową długość (= pusty) i test „-n”, jeśli łańcuch nie jest pusty.
Przypadki testowe :
źródło
source=$1; target=$2
jesource=$(realpath $1); target=$(realpath $2)
realpath
jest zalecane,source=$(readlink -f $1)
itp., Jeśli realpath nie jest dostępny (niestandardowy)$source
i$target
podoba mi się to: `if [[-e $ 1]]; następnie source = $ (readlink -f $ 1); w innym przypadku źródło = 1 USD; fi if [[-e $ 2]]; następnie cel = $ (readlink -f $ 2); w przeciwnym razie cel = 2 USD; fi` W ten sposób funkcja może obsługiwać rzeczywiste / istniejące ścieżki względne, a także katalogi fikcyjne.readlink
ma-m
opcję, która właśnie to robi;)źródło
Jest wbudowany w Perla od 2001 roku, więc działa na prawie każdym systemie, jaki możesz sobie wyobrazić, nawet na VMS .
Ponadto rozwiązanie jest łatwe do zrozumienia.
Na przykład:
... działałoby dobrze.
źródło
say
nie był dostępny w perlu jako dziennik, ale można go tutaj skutecznie wykorzystać.perl -MFile::Spec -E 'say File::Spec->abs2rel(@ARGV)'
perl -MFile::Spec -e 'print File::Spec->abs2rel(@ARGV)' "$target"
iperl -MFile::Spec -e 'print File::Spec->abs2rel(@ARGV)' "$target" "$origin"
. Pierwszy jednowierszowy skrypt perla używa jednego argumentu (origin jest bieżącym katalogiem roboczym). Drugi jednowierszowy skrypt perla używa dwóch argumentów.perl
można znaleźć prawie wszędzie, choć odpowiedź jest nadal jednokierunkowa.Zakładając, że zainstalowałeś: bash, pwd, dirname, echo; wtedy relpath jest
Grałem w golfa w odpowiedzi od Pini i kilku innych pomysłów
Uwaga : Wymaga to, aby obie ścieżki były istniejącymi folderami. Pliki nie będą działać.
źródło
Python
os.path.relpath
jako funkcja powłokiCelem tego
relpath
ćwiczenia jest naśladowanie funkcji Pythona 2.7os.path.relpath
(dostępnej w Pythonie w wersji 2.6, ale działającej poprawnie tylko w 2.7), zgodnie z propozycją xni . W rezultacie niektóre wyniki mogą różnić się od funkcji przedstawionych w innych odpowiedziach.(Nie testowałem z nowymi liniami w ścieżkach tylko dlatego, że psuje to sprawdzanie poprawności w oparciu o wywołanie
python -c
z ZSH. Z pewnością byłoby to możliwe z pewnym wysiłkiem.)Jeśli chodzi o „magię” w Bash, dawno temu zrezygnowałem z poszukiwania magii w Bash, ale od tego czasu znalazłem całą magię, której potrzebuję, a potem trochę w ZSH.
W związku z tym proponuję dwa wdrożenia.
Pierwsza implementacja ma być w pełni zgodna z POSIX . Przetestowałem to
/bin/dash
na Debian 6.0.6 „Squeeze”. Działa również doskonale z/bin/sh
w systemie OS X 10.8.3, który w rzeczywistości jest wersją Bash 3.2 udającą powłokę POSIX.Druga implementacja to funkcja powłoki ZSH, która jest odporna na wiele cięć i innych niedogodności na ścieżkach. Jeśli masz dostęp do ZSH, jest to zalecana wersja, nawet jeśli wywołujesz go w formie skryptu przedstawionej poniżej (tj. Z shebangiem
#!/usr/bin/env zsh
) z innej powłoki.Na koniec napisałem skrypt ZSH, który weryfikuje dane wyjściowe
relpath
polecenia znalezionego w$PATH
danych przypadkach testowych podanych w innych odpowiedziach. Dodałem trochę pikanterii do tych testów, dodając spacje, tabulacje i znaki interpunkcyjne, takie jak! ? *
tu i tam, a także rzuciłem kolejny test z egzotycznymi znakami UTF-8 znalezionymi w vim-powerline .Funkcja powłoki POSIX
Po pierwsze, funkcja powłoki zgodna z POSIX. Działa z różnymi ścieżkami, ale nie czyści wielu ukośników ani nie rozpoznaje dowiązań symbolicznych.
Funkcja powłoki ZSH
Teraz bardziej niezawodna
zsh
wersja. Jeśli chcesz, aby rozwiązał argumenty na prawdziwych ścieżkach à larealpath -f
(dostępnych wcoreutils
pakiecie Linux ), zamień:a
wiersze 3 i 4 na:A
.Aby użyć tego w zsh, usuń pierwszy i ostatni wiersz i umieść go w katalogu, który znajduje się w twojej
$FPATH
zmiennej.Skrypt testowy
Na koniec skrypt testowy. Akceptuje jedną opcję, a mianowicie
-v
włączenie pełnego wyjścia.źródło
/
Obawiam się, że nie działa, gdy kończy się pierwsza ścieżka .Powyższy skrypt powłoki został zainspirowany pini's (Dzięki!). Wywołuje błąd w module podświetlania składni Przepełnienia stosu (przynajmniej w mojej ramce podglądu). Dlatego zignoruj, jeśli wyróżnianie jest nieprawidłowe.
Niektóre uwagi:
Poza wymienionymi sekwencjami odwrotnego ukośnika, ostatni wiersz funkcji „relPath” wypisuje ścieżki zgodne z pythonem:
Ostatni wiersz można zastąpić (i uprościć) wierszem
Wolę to drugie, ponieważ
Nazwy plików mogą być bezpośrednio dołączane do ścieżek katalogu uzyskanych przez relPath, np .:
Dowiązania symboliczne w tym samym katalogu utworzonym za pomocą tej metody nie mają brzydkiego
"./"
tekstu przed nazwą pliku.Lista kodów dla testów regresji (wystarczy dołączyć ją do skryptu powłoki):
źródło
Niewiele odpowiedzi tutaj jest praktycznych na co dzień. Ponieważ bardzo trudno jest to zrobić poprawnie w czystym bashu, proponuję następujące, niezawodne rozwiązanie (podobne do jednej sugestii ukrytej w komentarzu):
Następnie możesz uzyskać ścieżkę względną na podstawie bieżącego katalogu:
lub możesz określić, aby ścieżka była względna do danego katalogu:
Jedyną wadą jest to, że wymaga Pythona, ale:
Należy pamiętać, że rozwiązania, które zawierają
basename
lubdirname
niekoniecznie muszą być lepsze, ponieważ wymagającoreutils
zainstalowania. Jeśli ktoś ma czystebash
rozwiązanie, które jest niezawodne i proste (zamiast zawiłej ciekawości), byłbym zaskoczony.źródło
Ten skrypt daje prawidłowe wyniki tylko dla danych wejściowych, które są ścieżkami bezwzględnymi lub ścieżkami względnymi bez
.
lub..
:źródło
Chciałbym użyć Perla do tego nie tak trywialnego zadania:
źródło
perl -MFile::Spec -e "print File::Spec->abs2rel('$absolute','$current')"
aby cytować wartość absolutną i bieżącą.relative=$(perl -MFile::Spec -e 'print File::Spec->abs2rel(@ARGV)' "$absolute" "$current")
. To gwarantuje, że same wartości nie mogą zawierać kodu perla!Nieznaczna poprawa w odpowiedziach kasku i Pini , która lepiej gra ze spacjami i pozwala na przejście względnych ścieżek:
źródło
test.sh:
Testowanie:
źródło
readlink
na$(pwd)
.Kolejne rozwiązanie, czysta
bash
+ GNUreadlink
do łatwego użycia w następującym kontekście:Działa to w prawie wszystkich obecnych systemach Linux. Jeśli
readlink -m
nie działa po twojej stronie, spróbujreadlink -f
zamiast tego. Zobacz także https://gist.github.com/hilbix/1ec361d00a8178ae8ea0, aby uzyskać możliwe aktualizacje:Uwagi:
*
lub?
.ln -s
:relpath / /
daje,.
a nie pusty ciągrelpath a a
dajea
, nawet jeślia
jest to katalogreadlink
jest wymagane do kanonizacji ścieżek.readlink -m
temu działa również dla jeszcze nieistniejących ścieżek.W starych systemach, gdzie
readlink -m
nie jest dostępny,readlink -f
kończy się niepowodzeniem, jeśli plik nie istnieje. Prawdopodobnie potrzebujesz takiego obejścia (niesprawdzone!):Nie jest to całkiem poprawne w przypadku
$1
dołączania.
lub..
nieistniejących ścieżek (jak w/doesnotexist/./a
), ale powinno obejmować większość przypadków.(Zamień
readlink -m --
powyżej nareadlink_missing
.)Edytuj ze względu na poniższą opinię
Oto test, czy ta funkcja rzeczywiście jest poprawna:
Zdziwiony? To są prawidłowe wyniki ! Nawet jeśli uważasz, że to nie pasuje do pytania, oto dowód, że jest to poprawne:
Bez wątpienia
../bar
jest to dokładna i jedyna poprawna ścieżka względna stronybar
widziana ze stronymoo
. Wszystko inne byłoby po prostu złe.Trywialne jest przyjęcie wyniku do pytania, które najwyraźniej zakłada, że
current
jest to katalog:To zwraca dokładnie to, o co prosiliśmy.
A zanim podniesiesz brew, oto nieco bardziej złożony wariant
relpath
(zauważ niewielką różnicę), który powinien również działać w przypadku składni URL (tak, że końcowy/
zachowuje się dzięki niektórymbash
-magicznym):A oto kontrole, aby wyjaśnić: To naprawdę działa zgodnie z instrukcją.
A oto, jak można to wykorzystać, aby uzyskać pożądany wynik pytania:
Jeśli znajdziesz coś, co nie działa, daj mi znać w komentarzach poniżej. Dzięki.
PS:
Dlaczego argumenty
relpath
„odwróconego” w przeciwieństwie do wszystkich innych odpowiedzi tutaj?Jeśli się zmienisz
do
wtedy możesz zostawić drugi parametr z dala, tak że BAZĄ jest bieżącym katalogiem / URL / czymkolwiek. To tylko zasada uniksowa, jak zwykle.
Jeśli Ci się nie podoba, wróć do systemu Windows. Dzięki.
źródło
Niestety, odpowiedź Marka Rushakoffa (teraz usunięta - odwoływała się do kodu stąd ) nie wydaje się działać poprawnie, gdy jest przystosowana do:
Sposób myślenia opisany w komentarzu można dopracować, aby działał poprawnie w większości przypadków. Mam zamiar założyć, że skrypt pobiera argument źródłowy (gdzie jesteś) i argument docelowy (gdzie chcesz się dostać) i że oba są absolutnymi ścieżkami lub oba są względne. Jeśli jeden jest bezwzględny, a drugi względny, najłatwiej jest poprzedzić względną nazwę bieżącym katalogiem roboczym - ale poniższy kod tego nie robi.
Strzec się
Poniższy kod jest bliski prawidłowego działania, ale nie jest do końca poprawny.
xyz/./pqr
”.xyz/../pqr
”../
ścieżek.Kod Dennisa jest lepszy, ponieważ naprawia 1 i 5 - ale ma te same problemy 2, 3, 4. Z tego powodu użyj kodu Dennisa (i głosuj wcześniej).
(Uwaga: POSIX udostępnia wywołanie systemowe,
realpath()
które rozwiązuje nazwy ścieżek, dzięki czemu nie ma w nich żadnych dowiązań symbolicznych. Zastosowanie tego do nazw wejściowych, a następnie użycie kodu Dennisa dałoby poprawną odpowiedź za każdym razem. otaczarealpath()
- już to zrobiłem - ale nie znam standardowego narzędzia, które to robi.)W tym celu uważam, że Perl jest łatwiejszy w użyciu niż powłoka, chociaż bash ma przyzwoitą obsługę tablic i prawdopodobnie mógłby to zrobić - ćwiczenie dla czytelnika. Tak więc, mając dwie kompatybilne nazwy, podziel je na części:
A zatem:
Skrypt testowy (nawiasy kwadratowe zawierają spację i tabulator):
Dane wyjściowe ze skryptu testowego:
Ten skrypt Perla działa dość dokładnie na Uniksie (nie uwzględnia wszystkich zawiłości nazw ścieżek Windows) w obliczu dziwnych danych wejściowych. Używa modułu
Cwd
i jego funkcjirealpath
do rozpoznania prawdziwej ścieżki nazw, które istnieją, i dokonuje analizy tekstowej ścieżek, które nie istnieją. We wszystkich przypadkach oprócz jednego generuje to samo wyjście, co skrypt Dennisa. Odmienny przypadek to:Dwa wyniki są równoważne - po prostu nie identyczne. (Dane wyjściowe pochodzą z delikatnie zmodyfikowanej wersji skryptu testowego - poniższy skrypt Perla po prostu drukuje odpowiedź, a nie dane wejściowe i odpowiedź jak w powyższym skrypcie.) Teraz: czy powinienem wyeliminować niedziałającą odpowiedź? Może...
źródło
/home/part1/part2
na/
ma zbyt wielu../
. W przeciwnym razie mój skrypt pasuje do twoich wyników, z wyjątkiem tego, że mój dodaje niepotrzebne.
na końcu tego, gdzie jest miejsce docelowe.
i nie używam./
na początku tych, które schodzą bez wchodzenia w górę.readlink /usr/bin/vi
daje/etc/alternatives/vi
, ale to kolejne dowiązanie symboliczne - podczas gdyreadlink -f /usr/bin/vi
daje/usr/bin/vim.basic
, które jest ostatecznym celem wszystkich dowiązań symbolicznych ...Podjąłem twoje pytanie jako wyzwanie, aby napisać to w „przenośnym” kodzie powłoki, tj
Działa na dowolnej powłoce zgodnej z POSIX (zsh, bash, ksh, ash, busybox, ...). Zawiera nawet testsuite, aby zweryfikować jego działanie. Kanonizacja ścieżek pozostawia się jako ćwiczenie. :-)
źródło
Moje rozwiązanie:
źródło
Oto moja wersja. Jest on oparty na odpowiedź przez @Offirmo . Sprawiłem, że jest kompatybilny z Dash i naprawiłem następującą awarię testową:
./compute-relative.sh "/a/b/c/de/f/g" "/a/b/c/def/g/"
->"../..f/g/"
Teraz:
CT_FindRelativePath "/a/b/c/de/f/g" "/a/b/c/def/g/"
->"../../../def/g/"
Zobacz kod:
źródło
Zgadnij, ten też powinien załatwić sprawę ... (zawiera wbudowane testy) :)
OK, spodziewaliśmy się trochę narzutu, ale robimy tutaj powłokę Bourne'a! ;)
źródło
Ten skrypt działa tylko na nazwach ścieżek. Nie wymaga żadnych plików. Jeśli przekazane ścieżki nie są bezwzględne, zachowanie jest nieco niezwykłe, ale powinno działać zgodnie z oczekiwaniami, jeśli obie ścieżki są względne.
Testowałem go tylko na systemie OS X, więc może nie być przenośny.
źródło
Ta odpowiedź nie dotyczy części Bash pytania, ale ponieważ próbowałem użyć odpowiedzi w tym pytaniu, aby zaimplementować tę funkcjonalność w Emacsie , wyrzucę ją tam.
Emacs ma w tym celu funkcję natychmiast po wyjęciu z pudełka:
źródło
relpath
funkcja) zachowuje się identyczniefile-relative-name
dla podanych przypadków testowych.Oto skrypt powłoki, który robi to bez wywoływania innych programów:
źródło: http://www.ynform.org/w/Pub/Relpath
źródło
Potrzebowałem czegoś takiego, ale to również rozwiązało dowiązania symboliczne. Odkryłem, że pwd ma w tym celu flagę -P. Dołączono fragment mojego skryptu. Jest w funkcji w skrypcie powłoki, stąd 1 $ i 2 $. Wartość wynikowa, która jest ścieżką względną od START_ABS do END_ABS, znajduje się w zmiennej UPDIRS. Skrypty cd do każdego katalogu parametrów w celu wykonania pwd -P, co oznacza również, że obsługiwane są względne parametry ścieżki. Na zdrowie, Jim
źródło