Sposób, w jaki zwykle dołączasz skrypt, to „source”
na przykład:
main.sh:
#!/bin/bash
source incl.sh
echo "The main script"
w tym sh:
echo "The included script"
Wynikiem wykonania polecenia „./main.sh” jest:
The included script
The main script
... Teraz, jeśli spróbujesz wykonać ten skrypt powłoki z innej lokalizacji, nie będzie w stanie znaleźć dołączenia, chyba że jest na twojej ścieżce.
Jaki jest dobry sposób, aby twój skrypt mógł znaleźć skrypt dołączania, zwłaszcza jeśli na przykład skrypt musi być przenośny?
Odpowiedzi:
Staram się, aby wszystkie moje skrypty były względem siebie względne. W ten sposób mogę użyć nazwy katalogu:
źródło
which $0
przyda sięBASH_SOURCE
tablicę i jak pierwszy element w tej tablicy zawsze wskazuje na bieżące źródło.Wiem, że jestem spóźniony na imprezę, ale powinno to działać bez względu na to, jak uruchomisz skrypt i używasz wyłącznie wbudowanych funkcji:
.
Komenda (kropka) jest aliasem dosource
,$PWD
jest ścieżką do katalogu roboczego,BASH_SOURCE
jest zmienną tablicową, której członkowie są źródłowymi nazwami plików,${string%substring}
usuwa najkrótsze dopasowanie $ substring z tyłu $ stringźródło
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
potrzebna? Mogę znaleźć taką potrzebę, jeśli polecenia są wklejane do monitu bash, aby uruchomić. Jednak jeśli działam w kontekście pliku skryptu, nie widzę potrzeby, aby to zrobić ...source
s (to znaczy, jeślisource
skryptsource
jest inny w innym katalogu i tak dalej, nadal działa).${BASH_SOURCE[0]}
tak, jak chcesz tylko najnowszą? Ponadto użycieDIR=$(dirname ${BASH_SOURCE[0]})
pozwoli ci pozbyć się warunkuAlternatywa dla:
jest:
.. zaletą jest brak zależności od dirname, co nie jest wbudowanym poleceniem (i nie zawsze jest dostępne w emulatorach)
źródło
basePath=$(dirname $0)
dał mi pustą wartość, gdy zawiera plik skryptowy zawierający źródło.Jeśli znajduje się w tym samym katalogu, możesz użyć
dirname $0
:źródło
$0
nazwa./t.sh
katalogu wraca.
; 2) pocd bin
zwróceniu.
jest nieprawidłowy.$BASH_SOURCE
nie jest lepsze.source "$(dirname $0)/incl.sh"
działa w tych przypadkachMyślę, że najlepszym sposobem na to jest użycie metody Chrisa Borana, ALE powinieneś obliczyć MY_DIR w ten sposób:
Aby zacytować strony podręcznika readlink:
Nigdy nie spotkałem przypadku użycia, w którym
MY_DIR
nie jest poprawnie obliczony. Jeśli uzyskasz dostęp do skryptu za pomocą dowiązania symbolicznego$PATH
, działa.źródło
$0
bezpośrednio?/home/you/script.sh
możeszcd /home
uruchomić skrypt, ponieważ w./you/script.sh
tym przypadkudirname $0
powróci./you
iMY_DIR=$(dirname $(readlink -f $0)); source $MY_DIR/incl.sh
Kombinacja odpowiedzi na to pytanie zapewnia najbardziej niezawodne rozwiązanie.
Pracował dla nas w skryptach klasy produkcyjnej z doskonałym wsparciem dla zależności i struktury katalogów:
Metoda obsługuje wszystkie te:
readlink
)${BASH_SOURCE[0]}
jest bardziej solidny niż$0
źródło
readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0
jeśli twój link do odczytu to BusyBox v1.01DIR=$(dirname $(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0)) # https://stackoverflow.com/a/34208365/
źródło
./incl.sh
rozwiązuje tę samą ścieżkę cocd
+pwd
. Jaka jest więc zaleta zmiany katalogu?Działa to nawet wtedy, gdy skrypt pochodzi:
źródło
BASH_SOURCE
to tablica ścieżek odpowiadająca stosowi wywołań. Pierwszy element odpowiada najnowszemu skryptowi na stosie, który jest obecnie wykonywanym skryptem. W rzeczywistości$BASH_SOURCE
wywoływana jako zmienna domyślnie rozwija się do pierwszego elementu, więc[0]
tutaj nie jest to konieczne. Zobacz ten link, aby uzyskać szczegółowe informacje.1. Najładniejszy
Zbadałem prawie każdą sugestię i oto najładniejsza, która dla mnie działała:
script_root=$(dirname $(readlink -f $0))
Działa nawet wtedy, gdy skrypt jest dowiązaniem symbolicznym do
$PATH
katalogu.Zobacz to w akcji tutaj: https://github.com/pendashteh/hcagent/blob/master/bin/hcagent
2. Najfajniejsza
To właściwie inna odpowiedź na tej samej stronie, ale dodaję ją również do mojej odpowiedzi!
2. Najbardziej niezawodny
Alternatywnie, w rzadkim przypadku, gdy te nie działały, oto podejście kuloodporne:
Możesz zobaczyć to w akcji w
taskrunner
źródle: https://github.com/pendashteh/taskrunner/blob/master/bin/taskrunnerMam nadzieję, że pomoże to komuś tam :)
Proszę również zostawić to jako komentarz, jeśli jeden nie działał dla Ciebie, i podaj swój system operacyjny i emulator. Dzięki!
źródło
Musisz określić lokalizację innych skryptów, nie ma innego wyjścia. Poleciłbym konfigurowalną zmienną u góry skryptu:
Alternatywnie możesz nalegać, aby użytkownik utrzymywał zmienną środowiskową wskazującą, gdzie znajduje się strona główna programu, na przykład PROG_HOME lub jakoś. Można to podać użytkownikowi automatycznie, tworząc skrypt z tymi informacjami w /etc/profile.d/, który będzie pozyskiwany przy każdym logowaniu użytkownika.
źródło
Sugeruję utworzenie skryptu setenv, którego jedynym celem jest zapewnienie lokalizacji dla różnych komponentów w całym systemie.
Wszystkie inne skrypty następnie źródły tego skryptu, dzięki czemu wszystkie lokalizacje są wspólne dla wszystkich skryptów za pomocą skryptu setenv.
Jest to bardzo przydatne podczas uruchamiania cronjobs. Podczas uruchamiania crona otrzymujesz minimalne środowisko, ale jeśli wszystkie skrypty crona najpierw dołączą skrypt setenv, będziesz w stanie kontrolować i synchronizować środowisko, w którym chcesz uruchamiać cronjobs.
Zastosowaliśmy taką technikę na naszej małpce, która była używana do ciągłej integracji w ramach projektu o wielkości około 2000 kSLOC.
źródło
Odpowiedź Steve'a jest zdecydowanie poprawną techniką, ale należy ją ponownie przeredagować, aby zmienna installpath znajdowała się w osobnym skrypcie środowiska, w którym składane są wszystkie takie deklaracje.
Następnie wszystkie skrypty źródłowe tego skryptu i powinny zmienić ścieżkę instalacji, wystarczy ją zmienić tylko w jednym miejscu. Sprawia, że wszystko jest bardziej bezpieczne. Boże, nienawidzę tego słowa! (-:
BTW Powinieneś naprawdę odwoływać się do zmiennej za pomocą $ {installpath}, gdy używasz jej w sposób pokazany w twoim przykładzie:
Jeśli nawiasy klamrowe zostaną pominięte, niektóre powłoki spróbują rozwinąć zmienną „installpath / incl.sh”!
źródło
Shell Script Loader to moje rozwiązanie.
Zapewnia funkcję o nazwie include (), która może być wywoływana wiele razy w wielu skryptach w celu odniesienia jednego skryptu, ale załaduje skrypt tylko raz. Funkcja może akceptować pełne lub częściowe ścieżki (skrypt jest przeszukiwany w ścieżce wyszukiwania). Zapewniona jest również podobna funkcja o nazwie load (), która bezwarunkowo ładuje skrypty.
Działa dla bash , ksh , pd ksh i zsh ze zoptymalizowanymi skryptami dla każdego z nich; oraz inne powłoki, które są ogólnie kompatybilne z oryginalnym sh, takie jak ash , dash , heirloom sh itp., poprzez uniwersalny skrypt, który automatycznie optymalizuje swoje funkcje w zależności od funkcji, które może zapewnić powłoka.
[Przyjęty przykład]
start.sh
To jest opcjonalny skrypt startowy. Umieszczenie tutaj metod uruchamiania jest tylko wygodą i można je zamiast tego umieścić w skrypcie głównym. Ten skrypt nie jest również potrzebny, jeśli skrypty mają zostać skompilowane.
main.sh
popiół
b.sh
wynik:
Najlepsze są oparte na nim skrypty, które można również skompilować w celu utworzenia pojedynczego skryptu z dostępnym kompilatorem.
Oto projekt, który go wykorzystuje: http://sourceforge.net/p/playshell/code/ci/master/tree/ . Może działać przenośnie z kompilacją skryptów lub bez. Kompilacja w celu utworzenia pojedynczego skryptu może się również zdarzyć i jest pomocna podczas instalacji.
Stworzyłem również prostszy prototyp dla każdej partii konserwatywnej, która może chcieć mieć krótkie wyobrażenie o tym, jak działa skrypt implementacyjny: https://sourceforge.net/p/loader/code/ci/base/tree/loader-include-prototype .bash . Jest mały i każdy może po prostu dołączyć kod do głównego skryptu, jeśli chce, jeśli jego kod ma działać z Bash 4.0 lub nowszym, a także nie używa
eval
.źródło
eval
kodu ed do ładowania zależności. Ojejeval
bloków u dołu jest zawsze uruchamiany. Tak więc, czy jest to potrzebne, czy nie, z pewnością używaeval
.eval
jest czystym złem i nie wiedzą, jak je wykorzystać.eval
to zło.Osobiście umieść wszystkie biblioteki w
lib
folderze i użyjimport
funkcji, aby je załadować.struktura folderów
script.sh
zawartośćPamiętaj, że ta funkcja importowania powinna znajdować się na początku skryptu, a następnie możesz łatwo importować swoje biblioteki w następujący sposób:
Dodaj pojedynczy wiersz u góry każdej biblioteki (tj. Utils.sh):
Teraz masz dostęp do funkcji wewnątrz
utils.sh
irequirements.sh
zscript.sh
DO ZROBIENIA: Napisz linker, aby zbudować pojedynczy
sh
plikźródło
Użycie źródła lub 0 $ nie da prawdziwej ścieżki do twojego skryptu. Możesz użyć identyfikatora procesu skryptu, aby pobrać jego prawdziwą ścieżkę
Używam tego skryptu i zawsze mi dobrze służył :)
źródło
Wszystkie moje skrypty startowe umieszczam w katalogu .bashrc.d. Jest to powszechna technika w takich miejscach jak /etc/profile.d itp.
Problem z rozwiązaniem wykorzystującym globbing ...
... czy możesz mieć listę plików, która jest „za długa”. Podejście takie jak ...
... działa, ale nie zmienia środowiska zgodnie z oczekiwaniami.
źródło
Oczywiście dla każdego z nich, ale myślę, że poniższy blok jest dość solidny. Wierzę, że obejmuje to „najlepszy” sposób na znalezienie katalogu i „najlepszy” sposób na wywołanie innego skryptu bash:
Może to być „najlepszy” sposób na włączenie innych skryptów. Jest to oparte na innej „najlepszej” odpowiedzi, która informuje skrypt bash, gdzie jest przechowywany
źródło
Powinno to działać niezawodnie:
źródło
musimy tylko znaleźć folder, w którym przechowywane są nasze pliki inc. sh i main.sh; po prostu zmień plik main.sh za pomocą tego:
main.sh
źródło
echo
i niewłaściwe użytkowaniesed
„sg
opcja. -1.SCRIPT_DIR=$(echo "$0" | sed "s/${SCRIPT_NAME}//")
a następniesource "${SCRIPT_DIR}incl.sh"
Według
man hier
odpowiednie miejsce na skrypt obejmuje/usr/local/lib/
Osobiście preferuję
/usr/local/lib/bash/includes
dla obejmuje. Istnieje biblioteka bash-helper do dołączania bibliotek w ten sposób:źródło
Możesz także użyć:
źródło