Mam skrypt, w którym nie chcę, aby zadzwonił, exit
jeśli jest pozyskiwany.
Myślałem o sprawdzeniu, czy $0 == bash
ma to jednak problemy, jeśli skrypt pochodzi z innego skryptu lub jeśli użytkownik pozyskuje go z innej powłoki ksh
.
Czy istnieje niezawodny sposób na wykrycie, czy skrypt jest pozyskiwany?
Odpowiedzi:
Wydaje się to być przenośne między Bash i Kornem:
Linia podobna do tej lub przypisanie podobne
pathname="$_"
(z późniejszym testem i działaniem) musi znajdować się w pierwszej linii skryptu lub w linii po shebang (która, jeśli jest używana, powinna być dla ksh, aby działała pod większość okoliczności).źródło
BASH_ENV
,$_
u góry skryptu będzie ostatnie polecenie uruchamiane zBASH_ENV
.$_
jest problemem.bash script
(wywołanie za pomocą pliku wykonywalnego powłoki, które to rozwiązanie błędnie zgłasza jako źródło ), oraz (b) (znacznie mniej prawdopodobne)echo bash; . script
(jeśli$_
zdarzy się, że pasuje do powłoki pozyskującej skrypt, to rozwiązanie błędnie zgłasza to jako powłoki w tle ). Tylko specjalne zmienne specyficzne dla powłoki (np.$BASH_SOURCE
) Pozwalają na niezawodne rozwiązania (wynika z tego, że nie ma niezawodnego rozwiązania zgodnego z POSIX). Możliwe jest , choć uciążliwe, wykonanie solidnego testu przekrojowego.Jeśli Twoja wersja Bash wie o zmiennej tablicowej BASH_SOURCE, spróbuj czegoś takiego:
źródło
${BASH_SOURCE[0]}
zamiast po prostu$BASH_SOURCE
? A${0}
vs$0
?BASH_SOURCE
jest zmienną tablicową (patrz instrukcja ), która przechowuje ślad stosu źródeł, gdzie${BASH_SOURCE[0]}
jest najnowsza. Nawiasy klamrowe są tutaj używane, aby powiedzieć bashowi, co jest częścią nazwy zmiennej. W$0
tym przypadku nie są konieczne , ale też nie ranią. ;)$array
,${array[0]}
domyślnie otrzymasz . Czy zatem istnieje powód [...]?Stabilne roztwory do
bash
,ksh
,zsh
, w tym przekroju powłoki jednego, plus stosunkowo niezawodne rozwiązanie POSIX :Podane numery wersji to te, na których funkcjonalność została zweryfikowana - prawdopodobnie te rozwiązania działają również na znacznie wcześniejszych wersjach - mile widziane opinie .
Używając tylko funkcji POSIX (takich jak in
dash
, który działa jak/bin/sh
w Ubuntu), nie ma solidnego sposobu na ustalenie, czy skrypt jest pozyskiwany - najlepsze przybliżenie znajduje się poniżej .Następujące linijki - wyjaśnienie poniżej; wersja wielopowłokowa jest złożona, ale powinna działać solidnie:
bash (zweryfikowany w wersjach 3.57 i 4.4.19)
ksh (zweryfikowane na 93u +)
zsh (zweryfikowany w 5.0.5) - pamiętaj, aby wywoływać to poza funkcją
cross-shell (bash, ksh, zsh)
Zgodny z POSIX ; nie jedna wkładka (pojedynczy potok) z powodów technicznych i nie w pełni niezawodne (patrz poniżej):
Wyjaśnienie:
grzmotnąć
Uwaga: technika została zaadaptowana z odpowiedzi user5754163 , ponieważ okazała się bardziej niezawodna niż oryginalne rozwiązanie,
[[ $0 != "$BASH_SOURCE" ]] && sourced=1 || sourced=0
[1]Bash zezwala na
return
instrukcje tylko z funkcji i, w zakresie najwyższego poziomu skryptu, tylko wtedy, gdy skrypt jest pozyskiwany .return
jest używany w zakresie najwyższego poziomu skryptu niepochodzącego ze źródła , emitowany jest komunikat o błędzie, a kod wyjścia jest ustawiony na1
.(return 0 2>/dev/null)
wykonuje sięreturn
w podpowłoce i pomija komunikat o błędzie; następnie kod zakończenia wskazuje, czy skrypt został pobrany (0
), czy nie (1
), który jest używany wraz z operatorami&&
i w||
celu odpowiedniego ustawieniasourced
zmiennej.return
w zakresie najwyższego poziomu skryptu źródłowego spowoduje zamknięcie skryptu.0
jakoreturn
operand; zauważa: za pomoc bashreturn [N]
: „Jeśli pominięto N, zwracany jest status ostatniego polecenia”. W rezultacie wcześniejsza wersja [która używała tylkoreturn
bez argumentu] daje niepoprawny wynik, jeśli ostatnia komenda w powłoce użytkownika ma niezerową wartość zwracaną.ksh
Zmienna specjalna
${.sh.file}
jest nieco analogiczna do$BASH_SOURCE
; zauważ, że${.sh.file}
powoduje błąd składniowy w bash, zsh i dash, więc upewnij się, że wykonałeś go warunkowo w skryptach wielopowłokowych.W przeciwieństwie do bash,
$0
i${.sh.file}
NIE gwarantuje się, że będą dokładnie identyczne w przypadku bez źródła, ponieważ$0
może to być ścieżka względna , podczas gdy${.sh.file}
zawsze jest to pełna ścieżka, dlatego$0
przed porównaniem należy ją rozwiązać na pełną ścieżkę.zsh
$ZSH_EVAL_CONTEXT
zawiera informacje o kontekście oceny - wywołaj to poza funkcją. Wewnątrz skryptu źródłowego [zakres najwyższego poziomu]$ZSH_EVAL_CONTEXT
kończy się na:file
.Zastrzeżenie: W środku na zmianę poleceń zsh Dokleja
:cmdsubst
, więc testu$ZSH_EVAL_CONTEXT
na:file:cmdsubst$
nie.Używanie tylko funkcji POSIX
Jeśli jesteś skłonny do pewnych założeń, można dokonać rozsądne, ale nie idiotoodporny zgadywać , czy skrypt jest pozyskiwane w oparciu o znajomość binarnych nazwy plików z muszli, które mogą być wykonywania skryptu .
W szczególności oznacza to, że takie podejście zawiedzie, jeśli twój skrypt jest pozyskiwany przez inny skrypt .
W sekcji „Jak obsługiwać wywołania źródłowe” w tej mojej odpowiedzi omówione są przypadki skrajne, których nie można obsługiwać tylko za pomocą funkcji POSIX.
To zależy od standardowego zachowania
$0
, którezsh
, na przykład czy nie wykazują.Zatem najbezpieczniejszym podejściem jest połączenie powyższych solidnych, specyficznych dla powłoki metod z rezerwowym rozwiązaniem dla wszystkich pozostałych powłok.
Porada dla Stéphane'a Desneux i jego odpowiedź na inspirację (przekształcenie mojego wyrażenia instrukcji między powłokami w instrukcję
sh
kompatybilnąif
i dodanie modułu obsługi innych powłok).[1] user1902689 odkrył, że
[[ $0 != "$BASH_SOURCE" ]]
daje fałszywie dodatni wynik po uruchomieniu skryptu znajdującego się w$PATH
pliku, przekazując jego nazwę pliku do plikubash
binarnego; na przykład,bash my-script
ponieważ$0
jest to wtedy sprawiedliwemy-script
, podczas gdy$BASH_SOURCE
jest pełna ścieżka . Choć zwykle nie korzystają z tej techniki do wywoływania skryptów w$PATH
- ci, że właśnie wywołuj je bezpośrednio (my-script
) - to jest pomocne, gdy łączy się z-x
do debugowania .źródło
Po przeczytaniu odpowiedzi @ DennisWilliamson istnieją pewne problemy, patrz poniżej:
Jak to pytanie oznacza ksh i grzmotnąć, jest jeszcze inna część tej odpowiedzi dotycząca ksh... patrz poniżej.
Prosty grzmotnąć sposób
Spróbujmy (w locie, bo ta bash mogłaby ;-):
Używam
source
zamiast tego wyłączania.
dla czytelności (jak.
jest alias dosource
):Pamiętaj, że numer procesu nie zmienia się, dopóki proces nie jest pozyskiwany :
Dlaczego nie skorzystać z
$_ == $0
porównania?Aby zapewnić wiele przypadków, zaczynam pisać prawdziwy skrypt:
Skopiuj to do pliku o nazwie
testscript
:Teraz możemy przetestować:
W porządku.
W porządku.
Ale w celu przetestowania skryptu przed dodaniem
-x
flagi:Lub użyć predefiniowanych zmiennych:
To już nie zadziała.
Przeniesienie komentarza z 5. linii na 6. dałoby bardziej czytelną odpowiedź:
Trudniej: ksh teraz...
Ponieważ nie używam ksh bardzo dużo, po przeczytaniu na stronie podręcznika, są moje próby:
Skopiuj to w
testfile.ksh
:Następnie uruchom go dwa razy:
i zobaczyć:
Istnieje pewna zmienna dziedziczona w źródle , ale tak naprawdę nic nie jest powiązane ...
Możesz nawet sprawdzić, czy
$SECONDS
jest blisko0.000
, ale to gwarantuje, że tylko ręcznie pozyskiwane przypadki ...Możesz nawet spróbować sprawdzić , kto jest rodzicem:
Umieść to w
testfile.ksh
:Niż:
lub
ps ho cmd $PPID
, ale działa to tylko dla jednego poziomu podsesji ...Przepraszam, nie mogłem znaleźć niezawodnego sposobu na zrobienie tego, poniżej ksh.
źródło
[ "$0" = "$BASH_SOURCE" ] || [ -z "$BASH_SOURCE" ]
dla skryptów wczytywanych przez pipe (cat script | bash
)..
to nie jest aliassource
, ale jest odwrotnie.source somescript.sh
jest Bash-ism i nie jest przenośny,. somescript.sh
jest POSIX i przenośny IIRC.BASH_SOURCE[]
Odpowiedź (bash-3.0 i nowsze) wydaje się najprostszy, choćBASH_SOURCE[]
jest nieudokumentowane do pracy na zewnątrz ciała funkcji (obecnie dzieje się z pracy, w niezgodzie ze strony człowieka).Najbardziej niezawodny sposób, jak sugeruje Wirawan Purwanto, to sprawdzenie
FUNCNAME[1]
w ramach funkcji :Następnie:
Jest to równoważne sprawdzeniu wyniku
caller
, wartościmain
isource
rozróżnieniu kontekstu osoby dzwoniącej. UżycieFUNCNAME[]
oszczędza przechwytywanie i analizowaniecaller
wyników. Musisz jednak znać lub obliczyć głębokość lokalnego połączenia, aby być poprawnym. Przypadki takie jak skrypt pochodzący z innej funkcji lub skryptu powodują, że tablica (stos) jest głębsza. (FUNCNAME
jest specjalną zmienną tablicową bash, powinna mieć ciągłe indeksy odpowiadające stosowi wywołań, o ile nie jest to nigdyunset
).(W wersji bash-4.2 i nowszych możesz użyć prostszej formy
${FUNCNAME[-1]}
zamiast ostatniego elementu w tablicy. Ulepszone i uproszczone dzięki komentarzowi Dennisa Williamsona poniżej).Jednak, jak stwierdzono, twoim problemem jest: „ Mam skrypt, w którym nie chcę, aby wywoływał„ wyjście ”, jeśli jest pozyskiwany ”. Typowym
bash
idiomem tej sytuacji jest:Jeśli skrypt jest pozyskiwany
return
, zakończy on skrypt i wróci do dzwoniącego.Jeśli skrypt jest wykonywany,
return
zwróci błąd (przekierowanie) iexit
zakończy działanie skryptu w normalny sposób. Zarównoreturn
iexit
może wziąć kod wyjścia, jeżeli jest to wymagane.Niestety nie działa to
ksh
(przynajmniej nie w wersji pochodnej AT&T, którą mam tutaj), traktuje sięreturn
jako równoważne,exit
jeśli wywoływane jest poza funkcją lub skryptem kropkowym.Zaktualizowano : To, co możesz zrobić we współczesnych wersjach,
ksh
to sprawdzenie specjalnej zmiennej.sh.level
ustawionej na głębokość wywołania funkcji. W przypadku wywoływanego skryptu będzie to początkowo wyłączone, w przypadku skryptu kropkowego zostanie ustawione na 1.To nie jest tak solidne jak wersja bash, musisz wywołać
issourced()
w pliku, z którego testujesz, na najwyższym poziomie lub na znanej głębokości funkcji.(Możesz być także zainteresowany tym kodem na github, który używa
ksh
funkcji dyscypliny i pewnych sztuczek związanych z debugowaniem pułapki w celu emulacjiFUNCNAME
tablicy bash .)Kanoniczna odpowiedź tutaj: http://mywiki.wooledge.org/BashFAQ/109 oferuje również
$-
jako kolejny wskaźnik (choć niedoskonały) stanu powłoki.Uwagi:
FUNCNAME[]
ale dopóki testowany jest tylko ostatni element w tej tablicy, nie ma dwuznaczności.pdksh
. Najbliższa rzecz, jaką mogę znaleźć, dotyczy tylko tegopdksh
, gdzie każde pozyskanie skryptu otwiera nowy deskryptor pliku (zaczynając od 10 dla oryginalnego skryptu). Niemal na pewno nie jest to coś, na czym chcesz polegać ...źródło
${FUNCNAME[(( ${#FUNCNAME[@]} - 1 ))]}
na zdobycie ostatniego (dolnego) przedmiotu ze stosu? Wtedy testowanie z „main” (negate for OP) było dla mnie najbardziej niezawodne.PROMPT_COMMAND
zestaw, pojawia się on jako ostatni indeksFUNCNAME
tablicy, jeśli uruchomięsource sourcetest.sh
. Odwracanie czeku (szukaniemain
jako ostatniego indeksu) wydaje się bardziej niezawodne:is_main() { [[ ${FUNCNAME[@]: -1} == "main" ]]; }
.FUNCNAME
jest dostępna tylko w funkcjach. Według moich testów zdeclare -p FUNCNAME
,bash
zachowuje się inaczej. v4.3 daje błąd poza funkcjami, podczas gdy v4.4 dajedeclare -a FUNCNAME
. Zarówno (!) Powrótmain
do${FUNCNAME[0]}
w głównym skrypcie (jeśli jest wykonywana), natomiast$FUNCNAME
nic nie daje. I: Jest tak wiele skryptów „ab” korzystających z$BASH_SOURCE
funkcji zewnętrznych, że wątpię, czy można to zmienić lub zmienić.Uwaga edytora: Rozwiązanie tej odpowiedzi działa solidnie, ale jest
bash
tylko. Można to usprawnić(return 2>/dev/null)
.TL; DR
Spróbuj wykonać
return
instrukcję. Jeśli skrypt nie jest pozyskiwany, spowoduje to błąd. Możesz złapać ten błąd i postępować tak, jak potrzebujesz.Umieść to w pliku i wywołaj, powiedzmy, test.sh:
Wykonaj to bezpośrednio:
Źródło:
Dla mnie działa to w Zsh i Bash.
Wyjaśnienie
return
Oświadczenie zgłosi błąd, jeśli spróbujesz uruchomić go poza funkcją lub jeśli skrypt nie pochodzi. Spróbuj tego z wiersza poleceń powłoki:Nie musisz widzieć tego komunikatu o błędzie, więc możesz przekierować dane wyjściowe do dev / null:
Teraz sprawdź kod wyjścia. 0 oznacza OK (nie wystąpiły błędy), 1 oznacza błąd:
Chcesz także wykonać
return
instrukcję wewnątrz podpowłoki. Kiedyreturn
instrukcja je uruchamia. . . dobrze . . . zwroty. Jeśli wykonasz go w podpowłoce, zwróci się z tej podpowłoki, zamiast wracać ze skryptu. Aby wykonać w podpowłoce, zawiń ją$(...)
:Teraz możesz zobaczyć kod wyjścia podpowłoki, który powinien wynosić 1, ponieważ w podpowłoce został zgłoszony błąd:
źródło
$ readlink $(which sh)
dash
$ . test.sh
This script is sourced.
$ ./test.sh
This script is sourced.
return
należy zrobić na najwyższym poziomie ( pubs.opengroup.org/onlinepubs/9699919799/utilities/… ). Wdash
traktuje płaszczareturn
na górnym poziomieexit
. Inne powłoki lubiąbash
lubzsh
nie zezwalająreturn
na najwyższym poziomie, co jest cechą takiej techniki.$
przed powłoką pośrednią. To znaczy użyj(return >/dev/null 2>&1)
zamiast$(return >/dev/null 2>&1)
- ale wtedy przestaje działać w bash.dash
tam, gdzie to rozwiązanie nie działa, działash
na przykład w systemie Ubuntu, na przykład to rozwiązanie zazwyczaj nie działash
. Rozwiązanie działa dla mnie w wersji Bash 3.2.57 i 4.4.5 - z$
wcześniejszą wersją lub bez niej(...)
(choć nigdy nie ma ku temu dobrego powodu$
).return
ing bez wyrażenia zwracanego przez explcit, psuje się podczas pisaniasource
skryptów tuż po źle wydanym poleceniu. Zaproponowano edycję rozszerzenia.FWIW, po przeczytaniu wszystkich innych odpowiedzi, wymyśliłem dla mnie następujące rozwiązanie:
Działa to dla wszystkich skryptów, które zaczynają się od,
#!/bin/bash
ale mogą być pozyskiwane z różnych powłok, a także do nauki niektórych informacji (takich jak ustawienia), które są przechowywane pozamain
funkcją.Zamiast ostatnich 2 wierszy możesz użyć następującego (moim zdaniem mniej czytelnego) kodu, aby nie ustawiać się
BASH_SOURCE
w innych powłokach i pozwalaćset -e
na pracę wmain
:Ten przepis skryptu ma następujące właściwości:
Jeśli jest wykonywany
bash
normalnym sposobem,main
jest wywoływany. Pamiętaj, że nie obejmuje to takiego połączeniabash -x script
(gdziescript
nie zawiera ścieżki), patrz poniżej.Jeśli pochodzi z
bash
,main
jest wywoływany tylko wtedy, gdy skrypt wywołujący ma taką samą nazwę. (Na przykład, jeśli sam się pozyskuje lub przezbash -c 'someotherscript "$@"' main-script args..
gdziemain-script
musi być, cotest
widzi jako$BASH_SOURCE
).Jeśli jest pozyskiwany / wykonywany / czytany /
eval
edytowany przez powłokę inną niżbash
,main
nie jest wywoływany (BASH_SOURCE
zawsze jest inny niż$0
).main
nie jest wywoływany, jeślibash
czyta skrypt ze standardowego wejścia, chyba że ustawisz$0
pusty ciąg znaków tak:( exec -a '' /bin/bash ) <script
Wywoływane przez
bash
przy pomocyeval
(eval "`cat script`"
wszystkie cytaty są ważne! ) Z innego skryptumain
. Jeślieval
jest uruchamiany bezpośrednio z wiersza poleceń, jest to podobne do poprzedniego przypadku, w którym skrypt jest odczytywany ze standardowego wejścia. (BASH_SOURCE
jest puste, choć$0
zwykle jest/bin/bash
zmuszone do czegoś zupełnie innego).Jeśli
main
nie jest wywołany, zwraca returntrue
($?=0
).Nie opiera się to na nieoczekiwanym zachowaniu (wcześniej pisałem nieudokumentowane, ale nie znalazłem żadnej dokumentacji, której nie można
unset
ani zmienićBASH_SOURCE
):BASH_SOURCE
jest zarezerwowaną tablicą bash . Ale pozwolenieBASH_SOURCE=".$0"
na zmianę otworzyłoby bardzo niebezpieczną puszkę robaków, więc spodziewam się, że to nie będzie miało żadnego efektu (może z wyjątkiem niektórych brzydkich ostrzeżeń pojawiających się w niektórych przyszłych wersjachbash
).BASH_SOURCE
działałaby poza funkcjami. Jednak przeciwieństwo (że działa tylko w funkcjach) nie jest ani udokumentowane. Obserwacja jest taka, że to działa (testowane zbash
wersją v4.3 i v4.4, niestety nie mam już wersjibash
v3.x) i że zbyt wiele skryptów ulegnie awarii, jeśli$BASH_SOURCE
przestanie działać zgodnie z obserwacjami. Dlatego oczekuję, żeBASH_SOURCE
pozostanie tak samo, jak w przypadku przyszłych wersjibash
.( return 0 )
, co daje,0
jeśli pochodzą, a1
jeśli nie pochodzą. Jest to trochę nieoczekiwane nie tylko dla mnie i (zgodnie z tamtymi odczytami) POSIX mówi, żereturn
z podpowłoki jest niezdefiniowane zachowanie (areturn
tutaj wyraźnie z podpowłoki). Być może ta funkcja w końcu jest wystarczająco szeroko rozpowszechniona, że nie można jej już zmienić, ale AFAICS ma o wiele większą szansę, że niektóre przyszłebash
wersje przypadkowo zmienią zachowanie powrotu w tym przypadku.Niestety
bash -x script 1 2 3
nie działamain
. (Porównaj,script 1 2 3
gdziescript
nie ma ścieżki). Jako obejście można zastosować następujące rozwiązanie:bash -x "`which script`" 1 2 3
bash -xc '. script' "`which script`" 1 2 3
bash script 1 2 3
nie działamain
można uznać za funkcję.Zauważ, że
( exec -a none script )
wywołaniamain
(bash
nie przekazują go$0
do skryptu, w tym celu musisz użyć,-c
jak pokazano w ostatnim punkcie).Tak więc, z wyjątkiem niektórych przypadków narożnych,
main
jest wywoływany tylko wtedy, gdy skrypt jest wykonywany w zwykły sposób. Zwykle tego właśnie chcesz, szczególnie dlatego, że brakuje w niej skomplikowanego, trudnego do zrozumienia kodu.Dlaczego uważam, że jest to dobry ogólny sposób rozwiązania problemu
Jeśli masz coś, co może pochodzić z wielu powłok, musi być kompatybilne. Jednak (przeczytaj pozostałe odpowiedzi), ponieważ nie ma (łatwego do wdrożenia) przenośnego sposobu na wykrycie
source
ingerencji, powinieneś zmienić reguły .Wymuszając wykonanie skryptu
/bin/bash
, robisz to dokładnie.To rozwiązuje wszystkie przypadki, z wyjątkiem następujących przypadków, w których skrypt nie może zostać uruchomiony bezpośrednio:
/bin/bash
nie jest zainstalowany lub nie działa (i. E. w środowisku rozruchowym)curl https://example.com/script | $SHELL
bash
jest wystarczająco nowy. Zgłoszono, że ten przepis nie działa w przypadku niektórych wariantów. Upewnij się więc, czy działa on w twoim przypadku).Nie mogę jednak wymyślić żadnego prawdziwego powodu, dla którego byś go potrzebował, a także możliwości równoległego pozyskiwania dokładnie tego samego skryptu! Zwykle można go owinąć, aby wykonać
main
ręcznie. Tak:$SHELL -c '. script && main'
{ curl https://example.com/script && echo && echo main; } | $SHELL
$SHELL -c 'eval "`curl https://example.com/script`" && main'
echo 'eval "`curl https://example.com/script`" && main' | $SHELL
Notatki
Ta odpowiedź nie byłaby możliwa bez pomocy wszystkich innych odpowiedzi! Nawet niewłaściwe - co początkowo zmusiło mnie do opublikowania tego.
Aktualizacja: Edycja ze względu na nowe odkrycia znalezione w https://stackoverflow.com/a/28776166/490291
źródło
/bin/sh
jest efektywniebash
w trybie POSIX, przypisywanie doBASH_SOURCE
zepsucia skryptu. W innych powłok (dash
,ksh
,zsh
), wywoływanie skryptu przekazując go jako argumentu plików bezpośrednio do powłoki wykonywalnych usterek (npzsh <your-script>
uczyni skrypt błędnie sądzi, że jest pozyskiwany ). (Już wspomnieć, że rurociągi usterki kodu we wszystkich muszli.). <your-script>
(sourcing) działa w zasadzie ze wszystkich powłok podobnych do POSIX, sensowne jest tylko, jeśli skrypt został wyraźnie napisany tak, aby korzystał tylko z funkcji POSIX, aby nie dopuścić do przerwania wykonywania funkcji specyficznych dla jednej powłoki w innych skorupkach; użycie linii shebang Bash (zamiast#!/bin/sh
) jest zatem mylące - przynajmniej bez rzucającego się w oczy komentarza. I odwrotnie, jeśli twój skrypt ma być uruchamiany tylko z Basha (nawet jeśli tylko z tego powodu, że nie rozważasz, które funkcje mogą nie być przenośne), lepiej odmówić wykonania w powłokach innych niż Bash.main
, ale robi to w tym przypadku! A kiedy pochodzą z tego/bin/sh
, cobash --posix
dzieje się w tym przypadku, dzieje się tak samo, i to również jest zupełnie złe.Działa to później w skrypcie i nie zależy od zmiennej _:
lub
źródło
Dam odpowiedź specyficzną dla BASH. Przepraszam, Korn Shell. Załóżmy, że twoja nazwa skryptu to
include2.sh
; następnie utworzyć funkcję wewnątrzinclude2.sh
nazywaam_I_sourced
. Oto moja wersja demonstracyjnainclude2.sh
:Teraz spróbuj wykonać to na wiele sposobów:
To działa bez wyjątku i nie używa kruchych
$_
rzeczy. Ta sztuczka wykorzystuje funkcję introspekcji BASH, tj. Wbudowane zmienneFUNCNAME
iBASH_SOURCE
; zobacz ich dokumentację na stronie podręcznika bash.Tylko dwa zastrzeżenia:
1) wezwanie do
am_I_called
musi się odbyć w skrypcie źródłowym, ale nie w ramach żadnej funkcji, aby nie${FUNCNAME[1]}
zwróciło czegoś innego. Tak ... mogłeś to sprawdzić${FUNCNAME[2]}
- ale utrudniasz sobie życie.2) funkcja
am_I_called
musi znajdować się w skrypcie źródłowym, jeśli chcesz dowiedzieć się, jaka jest nazwa dołączanego pliku.źródło
Chciałbym zasugerować niewielką korektę bardzo pomocnej odpowiedzi Dennisa , aby uczynić ją nieco bardziej przenośną, mam nadzieję:
ponieważ
[[
nie jest uznawany przez (nieco odbytu remanentnej IMHO) Debian POSIX kompatybilnym skorupkach,dash
. Można również potrzebować cudzysłowów w celu ochrony przed nazwami plików zawierającymi spacje, również we wspomnianej powłoce.źródło
$_
jest dość kruchy. Musisz to sprawdzić jako pierwszą rzecz, którą robisz w skrypcie. I nawet wtedy nie ma gwarancji, że będzie zawierała nazwę twojej powłoki (jeśli pochodzi) lub nazwę skryptu (jeśli zostanie wykonany).Na przykład, jeśli użytkownik ustawił
BASH_ENV
, wówczas u góry skryptu$_
znajduje się nazwa ostatniego polecenia wykonanego wBASH_ENV
skrypcie.Najlepszym sposobem, jaki znalazłem, jest użycie
$0
tego w następujący sposób:Niestety, ten sposób nie działa po wyjęciu z pudełka z powodu
functionargzero
opcja robi więcej niż sugeruje nazwa i jest domyślnie włączona.Aby obejść ten problem, włożyłem
unsetopt functionargzero
swój.zshenv
.źródło
Podążyłem za kompaktowym wyrazem mklement0 .
To miłe, ale zauważyłem, że może się nie powieść w przypadku ksh, gdy zostanie wywołany w następujący sposób:
(wydaje się, że jest to źródło i nie dzieje się tak, ponieważ wykonuje podpowłokę). Ale wyrażenie zadziała, aby to wykryć:
Ponadto, nawet jeśli wyrażenie jest zwarte, składnia nie jest kompatybilna ze wszystkimi powłokami.
Więc skończyłem z następującym kodem, który działa dla bash, zsh, dash i ksh
Dodaj obsługę egzotycznych muszli :)
źródło
ksh 93+u
,ksh ./myscript.sh
działa dobrze dla mnie (z moim oświadczeniem) - co w wersji używasz?/proc/$$/cmdline
) i koncentruje siędash
tylko na (który działa równieżsh
na przykład w Ubuntu). Jeśli chcesz poczynić pewne założenia, możesz zbadać pod$0
kątem rozsądnego - ale niepełnego - testu, który jest przenośny.sh
/dash
również w dodatku do mojej odpowiedzi.Nie sądzę, że istnieje jakiś przenośny sposób na zrobienie tego zarówno w ksh jak i bash. W bash można go wykryć za pomocą
caller
wyjścia, ale nie sądzę, że istnieje odpowiednik w ksh.źródło
$0
działabash
,ksh93
ipdksh
. Nie muszęksh88
testować.Potrzebowałem jednowierszowego, który działa na [mac, linux] z bash.version> = 3 i żadna z tych odpowiedzi nie pasuje do rachunku.
źródło
bash
Rozwiązanie działa dobrze (można uprościć do$BASH_SOURCE
), aleksh
rozwiązanie to nie jest solidna: Jeśli skrypt jest wchłaniany przez innego skryptu , dostaniesz fałszywie dodatni.Od razu do rzeczy: musisz ocenić, czy zmienna „$ 0” jest równa nazwie twojej powłoki.
Lubię to:
Przez SHELL :
Przez ŹRÓDŁO :
Trudno jest mieć w 100% przenośny sposób wykrywania, czy skrypt został pobrany, czy nie.
Jeśli chodzi o moje doświadczenie (7 lat z Shellscripting) , jedynym bezpiecznym sposobem (nie poleganie na zmiennych środowiskowych z PID i tak dalej, który nie jest bezpieczny ze względu na fakt, że jest to coś ZMIENNEGO ), powinieneś:
Obie opcje nie mogą być skalowane automatycznie, ale jest to bezpieczniejszy sposób.
Na przykład:
podczas generowania skryptu za pośrednictwem sesji SSH wartością zwracaną przez zmienną „$ 0” (przy użyciu źródła ) jest -bash .
LUB
źródło
/bin/bash -c '. ./check_source.sh'
dajeThe script WAS NOT sourced.
. Ten sam błąd:ln -s /bin/bash pumuckl; ./pumuckl -c '. ./check_source.sh'
->The script WAS NOT sourced.
Skończyło się na sprawdzeniu
[[ $_ == "$(type -p "$0")" ]]
Kiedy używasz
curl ... | bash -s -- ARGS
do uruchamiania zdalnego skryptu w locie, 0 $ będzie po prostubash
zamiast normalnego/bin/bash
po uruchomieniu rzeczywistego pliku skryptu, więc używamtype -p "$0"
do pokazania pełnej ścieżki bash.test:
źródło
Jest to pochodna niektórych innych odpowiedzi dotyczących „uniwersalnego” wsparcia dla wielu powłok. Jest to wprawdzie bardzo podobne w szczególności do https://stackoverflow.com/a/2942183/3220983 , choć nieco inne. Wadą tego jest to, że skrypt klienta musi respektować sposób jego użycia (tj. Najpierw eksportując zmienną). Siła polega na tym, że jest to proste i powinno działać „gdziekolwiek”. Oto szablon Twojej przyjemności wycinania i wklejania:
Uwaga: używam
export
tylko upewnij się, że ten mechanizm można rozszerzyć na podprocesy.źródło