Jak uzyskać ścieżkę do katalogu, w którym znajduje się skrypt Bash , wewnątrz tego skryptu?
Chcę użyć skryptu Bash jako programu uruchamiającego dla innej aplikacji. Chcę zmienić katalog roboczy na katalog, w którym znajduje się skrypt Bash, aby móc operować na plikach w tym katalogu, w następujący sposób:
$ ./application
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)"
- i usunąć go bez podstawienia polecenia -DIR="${DIR%x}"
.mkdir $'\n'
.dirname
i że katalog może zaczynać się od-
(np--help
.).DIR=$(reldir=$(dirname -- "$0"; echo x); reldir=${reldir%?x}; cd -- "$reldir" && pwd && echo x); DIR=${DIR%?x}
. Być może to przesada?Odpowiedzi:
jest użytecznym jednowierszowym, który daje pełną nazwę katalogu skryptu, bez względu na to, skąd jest wywoływany.
Będzie działał tak długo, jak ostatni komponent ścieżki użytej do znalezienia skryptu nie będzie dowiązaniem symbolicznym (łącza do katalogów są OK). Jeśli chcesz również rozwiązać jakiekolwiek łącza do samego skryptu, potrzebujesz rozwiązania wieloliniowego:
Ten ostatni będzie współpracować z dowolną kombinacją pseudonimy,
source
,bash -c
, dowiązania symboliczne, itd.Uwaga: jeśli
cd
przejdziesz do innego katalogu przed uruchomieniem tego fragmentu kodu, wynik może być niepoprawny!Uważaj również na
$CDPATH
gotchas i efekty uboczne wyjścia stderr, jeśli użytkownik inteligentnie przesłonił cd, aby zamiast tego przekierować wyjście do stderr (w tym sekwencje specjalne, na przykład podczas wywoływaniaupdate_terminal_cwd >&2
na komputerze Mac). Dodanie>/dev/null 2>&1
na końcu twojegocd
polecenia zadba o obie możliwości.Aby zrozumieć, jak to działa, spróbuj uruchomić ten bardziej szczegółowy formularz:
I wydrukuje coś takiego:
źródło
source <script>
ibash <script>
:DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
.cd
drukuje coś do STDOUT! Na przykład, jeśli$CDPATH
ma.
. Aby objąć tę sprawę, użyjDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"
dirname $(readlink -f $0)
to właściwe polecenie. Zobacz gist.github.com/tvlooy/cbfbdb111a4ebad8b93e dla przypadkudirname "$(readlink -f "$0")"
nie dodaje złożoności i jest sprawiedliwie bardziej odporny na minimalne problemy.readlink -f $0
dajereadlink: illegal option -- f
.Użyj
dirname "$0"
:użycie
pwd
samego nie będzie działać, jeśli nie uruchamiasz skryptu z katalogu, w którym się on znajduje.źródło
type -p
wtedy, gdy skrypt jest wykonywalny. Może to również otworzyć subtelną dziurę, jeśli skrypt jest wykonywany przy użyciu,bash test2.sh
a gdzieś jest inny skrypt o tej samej nazwie wykonywalny.bash
a linia hash-bang wyraźnie wspomina/bin/bash
, powiedziałbym, że można bezpiecznie polegać na baszizmach.dirname $0
polega na tym, że jeśli katalog jest katalogiem bieżącym, otrzymasz.
. W porządku, chyba że zamierzasz zmienić katalogi w skrypcie i oczekiwać, że użyjesz ścieżki, z której masz do czynienia,dirname $0
jakby była absolutna. Aby uzyskać bezwzględną ścieżkę:pushd `dirname $0` > /dev/null
,SCRIPTPATH=`pwd`
,popd > /dev/null
: pastie.org/1489386 (ale na pewno nie ma lepszego sposobu, aby rozwinąć tę ścieżkę?)dirname $0
jest to problem, jeśli przypiszesz go do zmiennej, a następnie użyjesz do uruchomienia skryptu podobnego do$dir/script.sh
; Wyobrażam sobie, że jest to przypadek użycia tego typu rzeczy w 90% przypadków../script.sh
działałoby dobrze.dirname
Komenda jest najbardziej podstawowym, po prostu parsowania ścieżkę do pliku off z$0
zmienna (nazwa skryptu):Ale, jak zauważył Matt B , zwrócona ścieżka różni się w zależności od sposobu wywołania skryptu.
pwd
nie wykonuje zadania, ponieważ mówi tylko o tym, jaki jest bieżący katalog, a nie katalog, w którym znajduje się skrypt. Ponadto, jeśli zostanie wykonane dowiązanie symboliczne do skryptu, otrzymasz ścieżkę (prawdopodobnie względną) do gdzie znajduje się link, a nie rzeczywisty skrypt.Inni wspomnieli o
readlink
poleceniu, ale najprościej możesz użyć:readlink
rozwiąże ścieżkę skryptu do ścieżki bezwzględnej z katalogu głównego systemu plików. Zatem wszelkie ścieżki zawierające pojedyncze lub podwójne kropki, tyldy i / lub dowiązania symboliczne zostaną przekształcone w pełną ścieżkę.Oto skrypt demonstrujący każdy z nich
whatdir.sh
:Uruchomienie tego skryptu w moim katalogu domowym przy użyciu ścieżki względnej:
Ponownie, ale używając pełnej ścieżki do skryptu:
Teraz zmieniamy katalogi:
I na koniec używając linku symbolicznego do wykonania skryptu:
źródło
readlink
nie będzie dostępny na niektórych platformach w domyślnej instalacji. Staraj się go nie używać, jeśli możeszexport SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
-f
nie jest rozpoznawany jako opcjareadlink
. Używaniestat -f
zamiast tego wykonuje zadanie. Dziękigreadlink
, co w zasadziereadlink
wszyscy znamy. Oto niezależna od platformy wersja:dir=`greadlink -f ${BASH_SOURCE[0]} || readlink -f ${BASH_SOURCE[0]}`
greadlink
można łatwo zainstalować za pomocą homebrew:brew install coreutils
Działa dla wszystkich wersji, w tym
source
” aka.
(kropka) operator.$0
jest modyfikowany z dzwoniącego."./script"
"/full/path/to/script"
"/some/path/../../another/path/script"
"./some/folder/script"
Alternatywnie, jeśli sam skrypt bash jest względnym dowiązaniem symbolicznym , chcesz go podążać i zwrócić pełną ścieżkę skryptu połączonego z:
SCRIPT_PATH
jest podane pełną ścieżką, bez względu na to, jak się nazywa.Tylko upewnij się, że znajdziesz to na początku skryptu.
Ten komentarz i kod Copyleft, licencja do wyboru na licencji GPL2.0 lub nowszej lub CC-SA 3.0 (CreativeCommons Share Alike) lub nowszej. (c) 2008. Wszelkie prawa zastrzeżone. Żadnej gwarancji. Zostałeś ostrzeżony.
http://www.gnu.org/licenses/gpl-2.0.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656
źródło
readlink -f $(dirname "${VIRTUAL_ENV}")
;dirname "${SCRIPT_PATH}"
&& pwd)? Ale i tak świetny skrypt!cd
opuścił swój bieżący katalog w nadziei,cd
że wróci później: Skrypt może nie mieć uprawnień do zmiany katalogu z powrotem na katalog, który był aktualny podczas jego wywołania. (To samo dotyczy pushd / popd)readlink -f
jest specyficzny dla GNU. BSDreadlink
nie ma tej opcji.([ ... ])
jest mniej wydajny niż[ ... ]
i nie ma żadnej korzyści z izolacji oferowanej w zamian za to uderzenie wydajności tutaj.Krótka odpowiedź:
lub ( najlepiej ):
źródło
source
użyła icd $(dirname $0)
jest łatwa do zapamiętania.${BASH_SOURCE[0]}
zamiast$0
współpracować zsource my/script.sh
${BASH_SOURCE[0]}
wcale nie jest zadowalający.${BASH_SOURCE:-0}
jest znacznie lepszy.Możesz użyć
$BASH_SOURCE
:Pamiętaj, że musisz go używać,
#!/bin/bash
a nie,#!/bin/sh
ponieważ jest to rozszerzenie Bash.źródło
./foo/script
, to$(dirname $BASH_SOURCE)
jest./foo
.realpath
polecenia, aby uzyskać pełną ścieżkę ./foo/script. Tak więcdirname $(realpath ./foo/script)
podam ścieżkę skryptu.To powinno to zrobić:
Działa to z dowiązaniami symbolicznymi i spacjami na ścieżce.
Zobacz strony podręcznika dla
dirname
ireadlink
.Ze ścieżki komentarzy wydaje się, że nie działa w systemie Mac OS. Nie mam pojęcia, dlaczego tak jest. Jakieś sugestie?
źródło
./script.sh
programy.
zamiast pełnej ścieżki katalogustat
zamiast tego. Ale wciąż pokazuje,.
czy jesteś w „tym” reż.coreutils
z Homebrew i użyć,greadlink
aby uzyskać-f
opcję na MacOS, ponieważ jest to * BSD pod okładkami, a nie Linux.DIR="$(dirname "$(readlink -f "$0")")"
pwd
można użyć do znalezienia bieżącego katalogu roboczego idirname
do znalezienia katalogu określonego pliku (polecenie, które zostało uruchomione, jest$0
, więcdirname $0
powinno dać ci katalog bieżącego skryptu).Jednak
dirname
daje właśnie część katalogów nazwy pliku, który nie jest bardziej prawdopodobne niż będzie względem bieżącego katalogu roboczego. Jeśli twój skrypt musi z jakiegoś powodu zmienić katalog, wynik zdirname
staje się bez znaczenia.Proponuję następujące:
W ten sposób otrzymujesz katalog absolutny, a nie względny.
Ponieważ skrypt zostanie uruchomiony w osobnej instancji bash, nie ma potrzeby późniejszego przywracania katalogu roboczego, ale jeśli z jakiegoś powodu chcesz z powrotem zmienić skrypt, możesz łatwo przypisać wartość
pwd
zmiennej przed zmień katalog, do wykorzystania w przyszłości.Chociaż tylko
rozwiązuje konkretny scenariusz w pytaniu, uważam, że ścieżka bezwzględna jest bardziej użyteczna ogólnie.
źródło
dirname $0
&& pwd)Mam dość ciągłego odwiedzania tej strony w celu skopiowania wklejania jednej linijki w zaakceptowanej odpowiedzi. Problem w tym, że niełatwo to zrozumieć i zapamiętać.
Oto łatwy do zapamiętania skrypt:
źródło
DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
realpath
z rozwiązywania „ręcznie” za pomocą pętlireadlink
? Nawetreadlink
stronaNote realpath(1) is the preferred command to use for canonicalization functionality.
realpath
wcześniejdirname
, a nie później? Jeśli sam plik skryptu jest dowiązaniem symbolicznym ... Dałby coś takiegoDIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
. Właściwie bardzo blisko odpowiedzi zaproponowanej przez Simona.Nie sądzę, że jest to tak łatwe, jak inni się na to wydawali.
pwd
nie działa, ponieważ bieżący katalog niekoniecznie jest katalogiem ze skryptem.$0
nie zawsze ma te informacje. Rozważ następujące trzy sposoby wywołania skryptu:W pierwszy i trzeci sposób
$0
nie ma pełnej informacji o ścieżce. W drugim i trzecimpwd
nie działa. Jedynym sposobem na uzyskanie katalogu w trzeci sposób byłoby uruchomienie ścieżki i znalezienie pliku z poprawnym dopasowaniem. Zasadniczo kod musiałby powtórzyć to, co robi system operacyjny.Jednym ze sposobów zrobienia tego, o co prosisz, byłoby po prostu zakodować dane w
/usr/share
katalogu i odwołać się do nich pełną ścieżką. Dane i tak nie powinny znajdować się w/usr/bin
katalogu, więc prawdopodobnie tak należy zrobić.źródło
źródło
$0
niepwd
ma gwarancji, że ma właściwe informacje, w zależności od sposobu wywołania skryptu.źródło
$BASH_SOURCE
ponad$0
, bo to wyraźny nawet dla czytelników nie dobrze zorientowani w bash.$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
Spowoduje to pobranie bieżącego katalogu roboczego w systemie Mac OS X 10.6.6:
źródło
Jest to specyficzne dla Linuksa, ale możesz użyć:
źródło
/proc/fd/$$/255
wydaje się wskazywać na tty, a nie na katalog. Na przykład w mojej bieżącej powłoce logowania odnoszą się wszystkie deskryptory plików 0, 1, 2 i 255/dev/pts/4
. W każdym razie instrukcja bash nie wspomina o fd 255, więc prawdopodobnie nierozsądne jest poleganie na tym zachowaniu. \realpath ${BASH_SOURCE[0]};
wydaje się najlepszą drogą.Oto jednolinijka zgodna z POSIX:
źródło
cd
jest skonfigurowany do drukowania nowej nazwy ścieżki.Próbowałem tych wszystkich i nic nie działało. Jeden był bardzo blisko, ale miał drobny błąd, który bardzo go zepsuł; zapomnieli zawinąć ścieżkę w cudzysłów.
Również wiele osób zakłada, że uruchamiasz skrypt z powłoki, więc zapominają, że po otwarciu nowego skryptu domyślnie jest to dom.
Wypróbuj ten katalog dla rozmiaru:
To dobrze, bez względu na to, jak i gdzie go uruchomisz:
Aby więc było to naprawdę przydatne, przejdź do katalogu uruchomionego skryptu:
źródło
ln -s ../bin64/foo /usr/bin/foo
).Oto prosty, poprawny sposób:
Wyjaśnienie:
${BASH_SOURCE[0]}
- pełna ścieżka do skryptu. Wartość tego będzie poprawna nawet podczas pozyskiwania skryptu, np.source <(echo 'echo $0')
Wypisuje bash , a zastąpienie go${BASH_SOURCE[0]}
wypisuje pełną ścieżkę skryptu. (Oczywiście zakłada to, że nie masz nic przeciwko Bashowi.)readlink -f
- Rekurencyjnie rozwiązuje wszelkie dowiązania symboliczne na określonej ścieżce. Jest to rozszerzenie GNU i nie jest dostępne (na przykład) w systemach BSD. Jeśli używasz komputera Mac, możesz użyć Homebrew, aby zainstalować GNUcoreutils
i zastąpić togreadlink -f
.I oczywiście
dirname
pobiera katalog macierzysty ścieżki.źródło
greadlink -f
niestety nie działa efektywnie podczassource
pisania skryptu na Macu :(Najkrótszym i najbardziej eleganckim sposobem na to jest:
To działałoby na wszystkich platformach i jest super czyste.
Więcej informacji można znaleźć w „W którym katalogu znajduje się ten skrypt bash? ”.
źródło
Użyłbym czegoś takiego:
źródło
sh
! Problem z prostymidirname "$0"
rozwiązaniami: jeśli skrypt znajduje się$PATH
i jest wywoływany bez ścieżki, dadzą zły wynik.PATH
,$0
będzie zawierać bezwzględną nazwę pliku. Jeśli skrypt zostanie wywołany z względną lub bezwzględną nazwą pliku zawierającą a/
,$0
będzie to zawierać.Jest to niewielka zmiana rozwiązania e-satis i 3bcdnlklvc04a wskazane w odpowiedzi :
Powinno to nadal działać we wszystkich wymienionych przypadkach.
Zapobiegnie to
popd
po awariipushd
, dzięki konsolebox.źródło
SCRIPT_DIR=''; pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && { SCRIPT_DIR=$PWD; popd > /dev/null; }
popd
w przypadkach (nawet rzadkich), w którychpushd
zawodzi. A w raziepushd
niepowodzenia, jaka powinna być wartośćSCRIPT_DIR
? Działanie może się różnić w zależności od tego, co może wydawać się logiczne lub co może preferować jeden użytkownik, ale na pewno robieniepopd
jest złe.pushd
popd
niebezpieczeństw można uniknąć po prostu upuszczając je i zamiast tego używająccd
+pwd
zawartego w podstawieniu polecenia.SCRIPT_DIR=$(...)
W systemach z rdzeniami GNU
readlink
(np. Linux):Nie ma potrzeby używania,
BASH_SOURCE
gdy$0
zawiera nazwę skryptu.źródło
dirname
połączenia. Potrzebny, jeśli ścieżka do katalogu zawiera spacje.źródło
$0
nie będzie działać w przypadku skryptu$_
warto wspomnieć jako alternatywę dla$0
. Jeśli uruchamiasz skrypt z Bash, zaakceptowaną odpowiedź można skrócić do:Pamiętaj, że musi to być pierwsza instrukcja w twoim skrypcie.
źródło
source
lub.
skrypt. W takich sytuacjach$_
zawierałby ostatni parametr ostatniego polecenia wykonanego przed.
.$BASH_SOURCE
działa za każdym razem.Porównałem wiele udzielonych odpowiedzi i opracowałem bardziej kompaktowe rozwiązania. Wydaje się, że radzą sobie ze wszystkimi szalonymi przypadkami, które wynikają z twojej ulubionej kombinacji:
script
,bash script
,bash -c script
,source script
, lub. script
Jeśli korzystasz z Linuksa, wydaje się, że użycie
proc
uchwytu jest najlepszym rozwiązaniem do zlokalizowania w pełni rozwiązanego źródła aktualnie uruchomionego skryptu (w sesji interaktywnej link wskazuje na odpowiedni/dev/pts/X
):Ma to trochę brzydoty, ale poprawka jest zwarta i łatwa do zrozumienia. Nie używamy tylko prymitywów bash, ale nie mam nic
readlink
przeciwko , ponieważ znacznie upraszcza to zadanie.echo X
DodajeX
na końcu łańcucha zmiennego tak, że wszystkie spacje spływu w nazwie pliku nie zjedzony, a podstawienie parametr${VAR%X}
na końcu linii pozbywa sięX
. Ponieważreadlink
dodaje nowy wiersz (który normalnie zostałby zjedzony podczas zastępowania poleceń, gdyby nie nasza poprzednia sztuczka), musimy się go również pozbyć. Najłatwiej to osiągnąć za pomocą$''
schematu cytowania, który pozwala nam używać sekwencji specjalnych, takich jak\n
reprezentować nowe wiersze (w ten sposób możesz łatwo tworzyć podstępnie nazwane katalogi i pliki).Powyższe powinno zaspokoić twoje potrzeby zlokalizowania aktualnie działającego skryptu w systemie Linux, ale jeśli nie masz do
proc
dyspozycji systemu plików lub próbujesz znaleźć w pełni rozwiązaną ścieżkę innego pliku, być może poniższy kod jest pomocny. To tylko niewielka modyfikacja powyższego jednowarstwowego. Jeśli grasz wokół dziwnych nazwach plików / katalogów, sprawdzając wyjście z obuls
ireadlink
ma charakter informacyjny, als
wyjście będzie „uproszczonej” ścieżki, zastępując?
na takie rzeczy jak nowej linii.źródło
/dev/pts/30
z bash na Ubuntu 14.10 Desktop.echo $resolved
, uratowałem go jakod
,chmod +x d
,./d
.#!/bin/bash
Spróbuj użyć:
źródło
$0
za${BASH_SOURCE[0]}
tak, że ta metoda będzie działać w dowolnym miejscu, w tym w funkcji.dirname
ponieważ ostatnia część$0
może być dowiązaniem symbolicznym wskazującym plik, który nie znajduje się w tym samym katalogu, co sam dowiązanie symboliczne. Rozwiązanie opisane w tej odpowiedzi po prostu pobiera ścieżkę do katalogu, w którym przechowywane jest dowiązanie symboliczne, a nie do katalogu docelowego. Ponadto w tym rozwiązaniu brakuje cytatów. To nie zadziała, jeśli ścieżka zawiera znaki specjalne.dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
Wypróbuj następujące rozwiązanie kompatybilne krzyżowo:
ponieważ polecenia takie jak
realpath
lubreadlink
mogą być niedostępne (w zależności od systemu operacyjnego).Uwaga: W Bash zaleca się stosowanie
${BASH_SOURCE[0]}
zamiast$0
, w przeciwnym razie ścieżka może się popsuć podczas pobierania pliku (source
/.
).Alternatywnie możesz wypróbować następującą funkcję w bash:
Ta funkcja przyjmuje 1 argument. Jeśli argument ma już ścieżkę bezwzględną, wypisz ją taką, jaka jest, w przeciwnym razie wypisz
$PWD
argument zmienna + nazwa pliku (bez./
prefiksu).Związane z:
źródło
realpath
Funkcja @Chris przyjmuje 1 argument. Jeśli argument ma już ścieżkę bezwzględną, wypisz ją taką, jaka jest, w przeciwnym razie wypisz$PWD
+ nazwa pliku (bez./
prefiksu).Myślę, że mam to. Spóźniam się na przyjęcie, ale myślę, że niektórzy docenią to, że będą tutaj, jeśli natkną się na ten wątek. Komentarze powinny wyjaśniać:
źródło
Oto krótkie sposoby na uzyskanie informacji o skrypcie:
Foldery i pliki:
Za pomocą tych poleceń:
I mam ten wynik:
Zobacz także: https://pastebin.com/J8KjxrPF
źródło
Działa to w bash-3.2:
Jeśli masz
~/bin
katalog w swoim katalogu$PATH
, znajdziesz goA
w tym katalogu. Źródła skryptu~/bin/lib/B
. Wiesz, gdzie dołączony skrypt jest względny w stosunku do oryginalnego, wlib
podkatalogu, ale nie w którym jest odniesiony do bieżącego katalogu użytkownika.Rozwiązuje to następujące (wewnątrz
A
):Nie ma znaczenia, gdzie jest użytkownik ani jak wywołuje skrypt, to zawsze zadziała.
źródło
which
jest bardzo dyskusyjna.type
,hash
i inne wbudowane funkcje robią to samo lepiej w bash.which
jest trochę bardziej przenośny, chociaż tak naprawdę nie jest taki sam, jakwhich
w innych powłokach, takich jak tcsh, który ma go jako wbudowany.which
będąc narzędziem zewnętrznym, nie ma powodu, by sądzić, że zachowuje się identycznie jak powłoka nadrzędna.Moim zdaniem najlepszym kompaktowym rozwiązaniem byłoby:
Nie można polegać na niczym innym niż Bash. Zastosowanie
dirname
,readlink
abasename
w konsekwencji doprowadzi do problemów z kompatybilnością, więc są one najlepiej unikać, jeśli to w ogóle możliwe.źródło
"$( cd "$( echo "${BASH_SOURCE[0]%/*}/" )"; pwd )"
. Jeśli nie masz problemów z katalogiem głównym. Również dlaczego w ogóle musisz używać echa?dirname
ibasename
są znormalizowane POSIX, więc dlaczego ich unikać? Linki:dirname
,basename