Jak ustawić bieżący katalog roboczy na katalog skryptu?

569

Piszę skrypt bash. Potrzebuję bieżącego katalogu roboczego, aby zawsze był katalogiem, w którym znajduje się skrypt.

Domyślne zachowanie polega na tym, że bieżącym katalogiem roboczym skryptu jest powłoka, z której go uruchamiam, ale nie chcę tego zachowania.

jameshfisher
źródło
Czy zastanawiałeś się nad umieszczeniem skryptu opakowania w katalogu / usr / bin na dysku CD w (zakodowanym na stałe) właściwym katalogu, a następnie wykonanie skryptu?
Dagg Nabbit
2
Dlaczego potrzebujesz katalogu skryptu? Prawdopodobnie istnieje lepszy sposób na rozwiązanie podstawowego problemu.
bstpierre
12
Chciałbym tylko zaznaczyć, że zachowanie, które nazywacie „oczywiście niepożądanym”, jest w rzeczywistości całkowicie konieczne - jeśli uruchomię myscript path/to/file, oczekuję, że skrypt oceni ścieżkę / do / pliku względem MOJEGO bieżącego katalogu, a nie tego, który katalog się wydarzy do znalezienia się. Co by się stało z uruchomieniem skryptu, ssh remotehost bash < ./myscriptjak wspomniano w BASH FAQ?
Gordon Davisson
3
cd "${BASH_SOURCE%/*}" || exit
caw

Odpowiedzi:

655
#!/bin/bash
cd "$(dirname "$0")"
ndim
źródło
7
dirname zwraca „.” podczas korzystania z bash w systemie Windows. Odpowiedź Pawła jest lepsza.
Tvaroh
7
Zwraca również „.” w Mac OSX
Ben Clayton,
4
Warto zauważyć, że rzeczy mogą się zepsuć, jeśli łącze symboliczne stanowi część $0. W skrypcie można oczekiwać na przykład, ../../że odniesie się do katalogu dwa poziomy powyżej lokalizacji skryptu, ale niekoniecznie dzieje się tak, jeśli w grę wchodzą dowiązania symboliczne.
Brian Gordon,
20
Jeśli skrypt został wywołany jako ./script, .to jest to poprawny katalog, a przejście do .niego skończy się również w samym katalogu, w którym się scriptznajduje, tj. W bieżącym katalogu roboczym.
ndim
6
Jeśli uruchomisz skrypt z bieżącego katalogu w ten sposób bash script.sh, wówczas wartość $0to script.sh. Jedynym sposobem, w jaki cdpolecenie będzie dla ciebie „działało”, jest to, że nie przejmujesz się nieudanymi poleceniami. Jeśli miałbyś użyć set -o errexit(aka:) set -eaby upewnić się, że twój skrypt nie wysadzi nieudanych poleceń, NIE zadziałałoby cd script.shto, ponieważ jest to błąd. Wiarygodnym sposobem [bash] jestcd "$(dirname ${BASH_SOURCE[0]})"
Bruno Bronosky,
322

Działa również:

cd "${0%/*}"

Składnia została dokładnie opisana w tej odpowiedzi StackOverflow.

Paul Schulz
źródło
17
Wyjaśnienie, jak to działa: stackoverflow.com/questions/6393551/...
kenorb
25
Nie zapomnij ująć w cudzysłów, jeśli ścieżka zawiera białe znaki. tj. cd „$ {0% / *}”
H.Rabiee
17
Nie powiedzie się to, jeśli skrypt zostanie wywołany z bash script.sh- $0będzie to po prostu nazwa pliku
Chris Watts
17
Powiedziałbym, że ta odpowiedź jest zbyt zwięzła. Prawie nic nie wiesz o składni. Przydałby się opis tego, jak to przeczytać.
fraxture
2
Ta sztuczka nie odradza także podpowłoki, więc jest dobra dla maniaków prędkości.
teknopaul
184

Wypróbuj następujące proste jednolinijki:


Dla wszystkich systemów UNIX / OSX / Linux

dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P)

Grzmotnąć

dir=$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)

Uwaga: Podwójny myślnik (-) jest używany w poleceniach do oznaczenia końca opcji poleceń, więc pliki zawierające myślniki lub inne znaki specjalne nie spowodują złamania polecenia.

Uwaga: w Bash użyj ${BASH_SOURCE[0]}na korzyść $0, w przeciwnym razie ścieżka może się złamać podczas jej pozyskiwania ( source/ .).


W przypadku systemu Linux, Mac i innych * BSD:

cd "$(dirname "$(realpath "$0")")";

Uwaga: realpathpowinien być domyślnie zainstalowany w najpopularniejszej dystrybucji Linuksa (np. Ubuntu), ale w niektórych może go brakować, więc musisz go zainstalować.

Uwaga: Jeśli używasz Bash, użyj ${BASH_SOURCE[0]}na korzyść $0, w przeciwnym razie ścieżka może się złamać podczas jej pozyskiwania ( source/ .).

W przeciwnym razie możesz spróbować czegoś takiego (użyje pierwszego istniejącego narzędzia):

cd "$(dirname "$(readlink -f "$0" || realpath "$0")")"

W przypadku systemu Linux:

cd "$(dirname "$(readlink -f "$0")")"

Używając linku do odczytu GNU na * BSD / Mac:

cd "$(dirname "$(greadlink -f "$0")")"

Uwaga: Musisz mieć coreutilszainstalowany (np. 1. Zainstaluj Homebrew , 2. brew install coreutils).


W bash

W bash możesz użyć do tego celu rozszerzeń parametrów , takich jak:

cd "${0%/*}"

ale to nie działa, jeśli skrypt jest uruchamiany z tego samego katalogu.

Alternatywnie możesz zdefiniować następującą funkcję w bash:

realpath () {
  [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

Ta funkcja przyjmuje 1 argument. Jeśli argument ma już ścieżkę bezwzględną, wypisz ją taką, jaka jest, w przeciwnym razie wypisz $PWDargument zmienna + nazwa pliku (bez ./prefiksu).

lub tutaj jest wersja zaczerpnięta z .bashrcpliku Debian :

function realpath()
{
    f=$@
    if [ -d "$f" ]; then
        base=""
        dir="$f"
    else
        base="/$(basename "$f")"
        dir=$(dirname "$f")
    fi
    dir=$(cd "$dir" && /bin/pwd)
    echo "$dir$base"
}

Związane z:

Zobacz też:

Jak mogę uzyskać zachowanie readlink -f GNU na komputerze Mac?

kenorb
źródło
4
znacznie lepsza odpowiedź niż popularne, ponieważ rozwiązuje syslink i uwzględnia różne systemy operacyjne. Dzięki!
Dennis
Dziękuję za tę odpowiedź. Pierwszy działał na moim Macu ... ale co robi przełącznik - w cppoleceniu, @kenorb?
TobyG
3
Podwójna myślnik ( --) jest używana w poleceniach do oznaczenia końca opcji polecenia, więc pliki zawierające myślniki lub inne znaki specjalne nie spowodują złamania polecenia. Spróbuj np. Utworzyć plik za pomocą touch "-test"i touch -- -test, a następnie usuń plik za pomocą rm "-test"i rm -- -testi zobacz różnicę.
kenorb
1
Chciałbym usunąć moje poparcie, gdybym mógł: realpath jest przestarzały unix.stackexchange.com/questions/136494/...
lsh
1
@lsh Mówi, że tylko realpathpakiet Debiana jest przestarzały, a nie GNU realpath. Jeśli uważasz, że to nie jest jasne, możesz zasugerować edycję.
kenorb
73
cd "$(dirname "${BASH_SOURCE[0]}")"

To jest łatwe. To działa.

Dan Molding
źródło
1
To powinna być zaakceptowana odpowiedź. Działa na Ubuntu w systemie OSX i Linux.
David Rissato Cruz
5
Działa to świetnie, gdy skrypt B pochodzi z innego skryptu A i musisz użyć ścieżek względem skryptu B. Dziękujemy.
Enrico
5
Działa to również w Cygwin dla tych z nas, którzy mają niefortunne dotknięcie Windows.
Bruno Bronosky,
3
Lubcd "${BASH_SOURCE%/*}" || exit
caw
Działa na macOS Mojave
Juan Boero
6

Akceptowana odpowiedź działa dobrze dla skryptów, które nie zostały dowiązane symbolicznie w innym miejscu, na przykład do $PATH.

#!/bin/bash
cd "$(dirname "$0")"

Jeśli jednak skrypt jest uruchamiany za pomocą dowiązania symbolicznego,

ln -sv ~/project/script.sh ~/bin/; 
~/bin/script.sh

Spowoduje to przejście do ~/bin/katalogu, a nie do ~/project/katalogu, co prawdopodobnie spowoduje uszkodzenie skryptu, jeśli celem cdjest uwzględnienie zależności względem~/project/

Bezpieczna odpowiedź dowiązania symbolicznego znajduje się poniżej:

#!/bin/bash
cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"

readlink -f jest wymagane do rozwiązania ścieżki bezwzględnej potencjalnie dowiązanego pliku.

Cudzysłowy są wymagane do obsługi ścieżek plików, które potencjalnie mogą zawierać spacje (zła praktyka, ale nie jest bezpiecznie zakładać, że tak nie będzie)

James McGuigan
źródło
4

Ten skrypt wydaje mi się działać:

#!/bin/bash
mypath=`realpath $0`
cd `dirname $mypath`
pwd

Wiersz polecenia pwd przypomina lokalizację skryptu jako bieżącego katalogu roboczego, bez względu na to, skąd go uruchamiam.

Amardeep AC9MF
źródło
3
realpathprawdopodobnie nie zostanie zainstalowany wszędzie. Co może nie mieć znaczenia, w zależności od sytuacji PO.
bstpierre
Mój system nie ma, realpathale ma taki, readlinkktóry wydaje się podobny.
Wstrzymano do odwołania.
2
Która dystrybucja nie ma domyślnie zainstalowanej ścieżki rzeczywistej? Ubuntu ma to.
kenorb
1
cd "`dirname $(readlink -f ${0})`"

źródło
Czy możesz wyjaśnić swoją odpowiedź?
Zulu
1
Chociaż ten kod może pomóc w rozwiązaniu problemu, nie wyjaśnia, dlaczego i / lub jak odpowiada na pytanie. Zapewnienie tego dodatkowego kontekstu znacznie poprawiłoby jego długoterminową wartość edukacyjną. Proszę edytować swoje odpowiedzi, aby dodać wyjaśnienie, w tym, co stosuje się ograniczenia i założenia.
Toby Speight
-5
echo $PWD

PWD jest zmienną środowiskową.

Maverick Holdem
źródło
5
Daje to tylko katalog wywoływany z, a nie katalog, w którym znajduje się skrypt.
dagelf,
-6

Jeśli potrzebujesz tylko wydrukować obecny katalog roboczy, możesz to zrobić.

$ vim test

#!/bin/bash
pwd
:wq to save the test file.

Udziel pozwolenia na wykonanie:

chmod u+x test

Następnie uruchom skrypt ./test, aby zobaczyć aktualny katalog roboczy.

Chandan
źródło
2
Pytanie brzmiało, jak upewnić się, że skrypt działa we własnym katalogu, w tym, jeśli nie jest to katalog roboczy. To nie jest odpowiedź. W każdym razie, dlaczego to robić zamiast po prostu ... biegać pwd? Wydaje mi się, że wiele zmarnowanych naciśnięć klawiszy.
underscore_d