Odwołaj się do pliku w tym samym katalogu skryptu, który znajduje się w $ PATH

33

Mam plik skryptu bash, który jest umieszczony w katalogu dodanym do $ PATH, dzięki czemu mogę wywoływać skrypt z dowolnego katalogu.

W tym samym katalogu co skrypt znajduje się inny plik tekstowy. Zastanawiam się, jak odwoływać się do pliku tekstowego w skrypcie?

Na przykład, jeśli skrypt ma po prostu wyświetlać zawartość pliku tekstowego, cat textfilenie zadziała, ponieważ podczas wywoływania skryptu z innego katalogu plik tekstowy nie zostanie znaleziony.

Tim
źródło
To pytanie odpowiada, jak niezawodnie uzyskać ścieżkę do plików skryptów bash, dodałem to do mojej ścieżki: stackoverflow.com/q/4774054/1695680
ThorSummoner

Odpowiedzi:

24

Powinny one działać tak samo, o ile nie ma dowiązań symbolicznych (w rozwinięciu ścieżki lub w samym skrypcie):

  • MYDIR="$(dirname "$(realpath "$0")")"

  • MYDIR="$(dirname "$(which "$0")")"

  • Dwuetapowa wersja dowolnego z powyższych:

    MYSELF="$(realpath "$0")"

    MYDIR="${MYSELF%/*}"

Jeśli po drodze do skryptu znajduje się dowiązanie symboliczne, wówczas whichotrzymasz odpowiedź bez uwzględnienia rozwiązania tego linku. Jeśli realpathnie jest domyślnie zainstalowany w twoim systemie, możesz go znaleźć tutaj .

[EDYCJA]: Wydaje się, że realpathnie ma przewagi nad readlink -f sugerowanymi przez Caleba , prawdopodobnie lepiej jest użyć tego drugiego. Moje testy czasowe wskazują, że jest to rzeczywiście szybsze.

rozcietrzewiacz
źródło
Nie ma problemu. Przy okazji, skąd realpathpochodzi twój system. (Dla innych, którzy go nie mają, możesz użyćreadlink -f
Caleb
@Caleb Właściwie myślałem, że należy do zestawu standardowych narzędzi GNU (coreutils), ale teraz widzę, że jest to osobny pakiet .
rozcietrzewiacz
@rozcietrzewiacz realpathpochodzi z przeszłości readlink -f(a nawet readlinkIIRC) był w GNU coreutils (było kilka podobnych narzędzi. readlink -fostatecznie stał się de facto standardem); realpathjest przechowywany tylko w celu zachowania zgodności ze skryptami, które nadal go używają.
Gilles 'SO - przestań być zły'
Jaka jest przewaga $(dirname "$(which "$0")")nad $(dirname $0)tym, że whichnie jest już obecny? Czy to nie to samo?
UlfR
readlink -fnie działa na Mac OS X 10.11.6, ale realpathdziała od razu po wyjęciu z pudełka.
Grav
9

Moje systemy nie mają, realpathjak sugeruje rozcietrzewiacz .

Możesz to zrobić za pomocą readlinkpolecenia. Zaletą korzystania z tego nad analizowaniem whichlub innymi rozwiązaniami jest to, że nawet jeśli wykonywana część ścieżki lub nazwa pliku była dowiązaniem symbolicznym, można znaleźć katalog, w którym znajduje się rzeczywisty plik.

MYDIR="$(dirname "$(readlink -f "$0")")"

Plik tekstowy można następnie odczytać w zmiennej takiej jak ta:

TEXTFILE="$(<$MYDIR/textfile)"
Caleb
źródło
@rozcietrzewiacz: Właściwie to nie odnosiłem się tylko do twojej whichsugestii. Normalne rozwiązanie tego problemu dotyczy tylko dirnamelub kombinacji cdiw pwdpodpowłoce. Readlink ma tutaj tę zaletę. realpathw readlink -fkażdym razie wydaje się być tylko opakowaniem .
Caleb
Nie wiem czym realpathsię różni readlink -f. Widzę tylko, że daje mi te same wyniki (w przeciwieństwie do which).
rozcietrzewiacz
Należy pamiętać, że readlink -f(z GNU coreutils) NIE wymaga istnienia ostatniego elementu ścieżki readlink -e, ale nie jest obsługiwany przez busybox readlink, co naśladuje zachowanie -eich -fopcji.
dragon788
8

$0w skrypcie będzie pełna ścieżka do skryptu, i dirnamezajmie pełną ścieżkę i da ci tylko katalog, więc możesz to zrobić, aby cat textile:

$ cat "$(dirname -- "$0")/textfile"
Michał Mrożek
źródło
Chociaż wydaje się realpath $0, że to nie działa , mylisz się mówiąc, że „ $0w skrypcie będzie pełna ścieżka do skryptu”.
rozcietrzewiacz
@roz W jaki sposób?
Michael Mrozek
1
$0to polecenie, w jakim zostało uruchomione, którym może być np ../script.sh.
rozcietrzewiacz
Tak więc faktycznie $(dirname "$0")zwraca względną ścieżkę do skryptu jako część wywołanego polecenia - nie ścieżkę bezwzględną. Może to prowadzić do problemów ze skryptami zmieniającymi katalogi podczas działania.
rozcietrzewiacz
@roz Ah, ciekawe. Sądzę więc, że nie spowodowałoby to problemu, ponieważ nazywa coś na ścieżce po imieniu, ale psuje inne rzeczy. Dzięki
Michael Mrozek
4

Możesz umieścić to na górze skryptu:

cd "${BASH_SOURCE%/*}" || exit

Wewnętrzna zmienna bash BASH_SOURCE jest w rzeczywistości tablicą nazw ścieżek. Jeśli rozwiniesz go jako prosty ciąg, np. „$ BASH_SOURCE”, otrzymasz pierwszy element, którym jest ścieżka do aktualnie wykonywanej funkcji lub skryptu.

Źródło: http://mywiki.wooledge.org/BashFAQ/028

David Kennedy
źródło
2

Próbowałem tych i prawdziwa ścieżka nie działała dla mnie. Rozwiązaniem, które wybrałem jest:

SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )

Co do tej pory działało dobrze. Chciałbym wiedzieć, czy są jakieś potencjalne problemy z tym podejściem.

Brettski
źródło
1
Dobrze, ale nie podąża za dowiązaniami symbolicznymi. Spróbuj tego:SCRIPT_DIR="$( cd "$(dirname "$( readlink -f ${BASH_SOURCE[0]} )")" >/dev/null 2>&1 && pwd)"
OronNavon
1

Używam:

#! /bin/sh -
dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P) || exit
dosomethingwith "${dir%/}/some-file"

Który jest POSIX i powinien działać tak długo, jak nazwa katalogu $0nie kończy się na znakach nowego wiersza, nie jest -i $CDPATHnie jest ustawiona (i być może kilka innych przypadków narożnych, jeśli skrypt nie został wyszukany $PATH).

Stéphane Chazelas
źródło
0

Zawsze używam whichdo znalezienia pełnej ścieżki pliku wykonywalnego z PATH. Na przykład:

which python

Jeśli połączysz to z dirnamepoleceniem, otrzymasz:

wp=`which python`
dn=`dirname $wp`
ls $dn
Michael Dillon
źródło