Jak wykryć, czy skrypt jest pozyskiwany

217

Mam skrypt, w którym nie chcę, aby zadzwonił, exitjeśli jest pozyskiwany.

Myślałem o sprawdzeniu, czy $0 == bashma 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?

Brianegge
źródło
2
Jakiś czas temu miałem podobny problem i rozwiązałem go, unikając „wyjścia” we wszystkich przypadkach; „kill -INT $$” bezpiecznie kończy skrypt w obu przypadkach.
JESii,
1
Czy zauważyłeś tę odpowiedź ? Jest podawany 5 lat później od zaakceptowanego, ale ma „baterie w zestawie”.
raratiru

Odpowiedzi:

73

Wydaje się to być przenośne między Bash i Kornem:

[[ $_ != $0 ]] && echo "Script is being sourced" || echo "Script is a subshell"

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).

Wstrzymano do odwołania.
źródło
10
Niestety nie ma gwarancji, że zadziała. Jeśli użytkownik ustawił BASH_ENV, $_u góry skryptu będzie ostatnie polecenie uruchamiane z BASH_ENV.
Mikel
30
To również nie zadziała, jeśli użyjesz bash do wykonania skryptu, np. $ Bash script.sh, wtedy $ _ będzie / bin / bash zamiast ./script.sh, co jest oczekiwanym przypadkiem, gdy skrypt zostanie wywołany w ten sposób: $ ./script.sh W każdym razie wykrycie za pomocą $_jest problemem.
Wirawan Purwanto,
2
Mogą być dołączone dodatkowe testy w celu sprawdzenia tych metod wywoływania.
Wstrzymano do odwołania.
8
Niestety, to źle! patrz moja odpowiedź
F. Hauri
8
Podsumowując: Chociaż takie podejście zwykle działa, nie jest ono jednak niezawodne ; kończy się niepowodzeniem w następujących 2 scenariuszach: (a) 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.
mklement0
170

Jeśli Twoja wersja Bash wie o zmiennej tablicowej BASH_SOURCE, spróbuj czegoś takiego:

# man bash | less -p BASH_SOURCE
#[[ ${BASH_VERSINFO[0]} -le 2 ]] && echo 'No BASH_SOURCE array variable' && exit 1

[[ "${BASH_SOURCE[0]}" != "${0}" ]] && echo "script ${BASH_SOURCE[0]} is being sourced ..."
Barroyo
źródło
11
To może najczystszy sposób, ponieważ $ BASH_SOURCE jest właśnie w tym celu przeznaczony.
con-f-use
4
Zauważ, że to nie zadziała pod ksh, co jest warunkiem określonym przez OP.
Wstrzymano do odwołania.
2
Czy istnieje powód, aby użyć ${BASH_SOURCE[0]}zamiast po prostu $BASH_SOURCE? A ${0}vs $0?
hraban
4
BASH_SOURCEjest 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 $0tym przypadku nie są konieczne , ale też nie ranią. ;)
Konrad
4
@Konrad, a jeśli rozwiniesz $array, ${array[0]}domyślnie otrzymasz . Czy zatem istnieje powód [...]?
Charles Duffy,
133

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/shw 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)

    (return 0 2>/dev/null) && sourced=1 || sourced=0
  • ksh (zweryfikowane na 93u +)

    [[ $(cd "$(dirname -- "$0")" && 
       printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] &&
         sourced=1 || sourced=0
  • zsh (zweryfikowany w 5.0.5) - pamiętaj, aby wywoływać to poza funkcją

    [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
  • cross-shell (bash, ksh, zsh)

    ([[ -n $ZSH_EVAL_CONTEXT && $ZSH_EVAL_CONTEXT =~ :file$ ]] || 
     [[ -n $KSH_VERSION && $(cd "$(dirname -- "$0")" &&
        printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] || 
     [[ -n $BASH_VERSION ]] && (return 0 2>/dev/null)) && sourced=1 || sourced=0
  • Zgodny z POSIX ; nie jedna wkładka (pojedynczy potok) z powodów technicznych i nie w pełni niezawodne (patrz poniżej):

    sourced=0
    if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
      case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
    elif [ -n "$KSH_VERSION" ]; then
      [ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1
    elif [ -n "$BASH_VERSION" ]; then
      (return 0 2>/dev/null) && sourced=1 
    else # All other shells: examine $0 for known shell binary filenames
      # Detects `sh` and `dash`; add additional shell filenames as needed.
      case ${0##*/} in sh|dash) sourced=1;; esac
    fi

Wyjaśnienie:


grzmotnąć

(return 0 2>/dev/null) && sourced=1 || sourced=0

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 returninstrukcje tylko z funkcji i, w zakresie najwyższego poziomu skryptu, tylko wtedy, gdy skrypt jest pozyskiwany .

    • Jeśli returnjest 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 na 1.
  • (return 0 2>/dev/null)wykonuje się returnw 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 ustawienia sourcedzmiennej.

    • Korzystanie z podpowłoki jest konieczne, ponieważ wykonanie returnw zakresie najwyższego poziomu skryptu źródłowego spowoduje zamknięcie skryptu.
    • Wskazówka kapelusza dla @Haozhun , który uczynił komendę bardziej niezawodną poprzez jawne użycie 0jako returnoperand; zauważa: za pomoc bash return [N]: „Jeśli pominięto N, zwracany jest status ostatniego polecenia”. W rezultacie wcześniejsza wersja [która używała tylko returnbez argumentu] daje niepoprawny wynik, jeśli ostatnia komenda w powłoce użytkownika ma niezerową wartość zwracaną.

ksh

[[ \
   $(cd "$(dirname -- "$0")" && printf '%s' "${PWD%/}/")$(basename -- "$0") != \
   "${.sh.file}" \
]] && 
sourced=1 || sourced=0

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, $0i ${.sh.file}NIE gwarantuje się, że będą dokładnie identyczne w przypadku bez źródła, ponieważ $0może to być ścieżka względna , podczas gdy ${.sh.file}zawsze jest to pełna ścieżka, dlatego $0przed porównaniem należy ją rozwiązać na pełną ścieżkę.


zsh

[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0

$ZSH_EVAL_CONTEXTzawiera 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_CONTEXTna :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óre zsh, 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ę shkompatybilną ifi dodanie modułu obsługi innych powłok).

sourced=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
  case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
elif [ -n "$KSH_VERSION" ]; then
  [ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1
elif [ -n "$BASH_VERSION" ]; then
  (return 0 2>/dev/null) && sourced=1 
else # All other shells: examine $0 for known shell binary filenames
  # Detects `sh` and `dash`; add additional shell filenames as needed.
  case ${0##*/} in sh|dash) sourced=1;; esac
fi

[1] user1902689 odkrył, że [[ $0 != "$BASH_SOURCE" ]]daje fałszywie dodatni wynik po uruchomieniu skryptu znajdującego się w $PATHpliku, przekazując jego nazwę pliku do plikubash binarnego; na przykład, bash my-scriptponieważ $0jest to wtedy sprawiedliwe my-script, podczas gdy $BASH_SOURCEjest 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 -xdo debugowania .

mklement0
źródło
1
chwała za tak kompleksową odpowiedź.
DrUseful
75

Po przeczytaniu odpowiedzi @ DennisWilliamson istnieją pewne problemy, patrz poniżej:

Jak to pytanie oznacza i , jest jeszcze inna część tej odpowiedzi dotycząca ... patrz poniżej.

Prosty sposób

[ "$0" = "$BASH_SOURCE" ]

Spróbujmy (w locie, bo ta bash mogłaby ;-):

source <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)

bash <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 16229 is own (/dev/fd/63, /dev/fd/63)

Używam sourcezamiast tego wyłączania .dla czytelności (jak .jest alias do source):

. <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)

Pamiętaj, że numer procesu nie zmienia się, dopóki proces nie jest pozyskiwany :

echo $$
29301

Dlaczego nie skorzystać z $_ == $0porównania?

Aby zapewnić wiele przypadków, zaczynam pisać prawdziwy skrypt:

#!/bin/bash

# As $_ could be used only once, uncomment one of two following lines

#printf '_="%s", 0="%s" and BASH_SOURCE="%s"\n' "$_" "$0" "$BASH_SOURCE"
[[ "$_" != "$0" ]] && DW_PURPOSE=sourced || DW_PURPOSE=subshell

[ "$0" = "$BASH_SOURCE" ] && BASH_KIND_ENV=own || BASH_KIND_ENV=sourced;
echo "proc: $$[ppid:$PPID] is $BASH_KIND_ENV (DW purpose: $DW_PURPOSE)"

Skopiuj to do pliku o nazwie testscript:

cat >testscript   
chmod +x testscript

Teraz możemy przetestować:

./testscript 
proc: 25758[ppid:24890] is own (DW purpose: subshell)

W porządku.

. ./testscript 
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)

source ./testscript 
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)

W porządku.

Ale w celu przetestowania skryptu przed dodaniem -xflagi:

bash ./testscript 
proc: 25776[ppid:24890] is own (DW purpose: sourced)

Lub użyć predefiniowanych zmiennych:

env PATH=/tmp/bintemp:$PATH ./testscript 
proc: 25948[ppid:24890] is own (DW purpose: sourced)

env SOMETHING=PREDEFINED ./testscript 
proc: 25972[ppid:24890] is own (DW purpose: sourced)

To już nie zadziała.

Przeniesienie komentarza z 5. linii na 6. dałoby bardziej czytelną odpowiedź:

./testscript 
_="./testscript", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26256[ppid:24890] is own

. testscript 
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced

source testscript 
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced

bash testscript 
_="/bin/bash", 0="testscript" and BASH_SOURCE="testscript"
proc: 26317[ppid:24890] is own

env FILE=/dev/null ./testscript 
_="/usr/bin/env", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26336[ppid:24890] is own

Trudniej: teraz...

Ponieważ nie używam bardzo dużo, po przeczytaniu na stronie podręcznika, są moje próby:

#!/bin/ksh

set >/tmp/ksh-$$.log

Skopiuj to w testfile.ksh:

cat >testfile.ksh
chmod +x testfile.ksh

Następnie uruchom go dwa razy:

./testfile.ksh
. ./testfile.ksh

ls -l /tmp/ksh-*.log
-rw-r--r-- 1 user user   2183 avr 11 13:48 /tmp/ksh-9725.log
-rw-r--r-- 1 user user   2140 avr 11 13:48 /tmp/ksh-9781.log

echo $$
9725

i zobaczyć:

diff /tmp/ksh-{9725,9781}.log | grep ^\> # OWN SUBSHELL:
> HISTCMD=0
> PPID=9725
> RANDOM=1626
> SECONDS=0.001
>   lineno=0
> SHLVL=3

diff /tmp/ksh-{9725,9781}.log | grep ^\< # SOURCED:
< COLUMNS=152
< HISTCMD=117
< LINES=47
< PPID=9163
< PS1='$ '
< RANDOM=29667
< SECONDS=23.652
<   level=1
<   lineno=1
< SHLVL=2

Istnieje pewna zmienna dziedziczona w źródle , ale tak naprawdę nic nie jest powiązane ...

Możesz nawet sprawdzić, czy $SECONDSjest blisko 0.000, ale to gwarantuje, że tylko ręcznie pozyskiwane przypadki ...

Możesz nawet spróbować sprawdzić , kto jest rodzicem:

Umieść to w testfile.ksh:

ps $PPID

Niż:

./testfile.ksh
  PID TTY      STAT   TIME COMMAND
32320 pts/4    Ss     0:00 -ksh

. ./testfile.ksh
  PID TTY      STAT   TIME COMMAND
32319 ?        S      0:00 sshd: user@pts/4

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 .

F. Hauri
źródło
[ "$0" = "$BASH_SOURCE" ] || [ -z "$BASH_SOURCE" ]dla skryptów wczytywanych przez pipe ( cat script | bash).
hakre
2
Pamiętaj, że .to nie jest alias source, ale jest odwrotnie. source somescript.shjest Bash-ism i nie jest przenośny, . somescript.shjest POSIX i przenośny IIRC.
dragon788,
32

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 :

function mycheck() { declare -p FUNCNAME; }
mycheck

Następnie:

$ bash sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="main")'
$ . sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="source")'

Jest to równoważne sprawdzeniu wyniku caller, wartości maini sourcerozróżnieniu kontekstu osoby dzwoniącej. Użycie FUNCNAME[]oszczędza przechwytywanie i analizowanie callerwynikó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. ( FUNCNAMEjest specjalną zmienną tablicową bash, powinna mieć ciągłe indeksy odpowiadające stosowi wywołań, o ile nie jest to nigdy unset).

function issourced() {
    [[ ${FUNCNAME[@]: -1} == "source" ]]
}

(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 bashidiomem tej sytuacji jest:

return 2>/dev/null || exit

Jeśli skrypt jest pozyskiwany return, zakończy on skrypt i wróci do dzwoniącego.

Jeśli skrypt jest wykonywany, returnzwróci błąd (przekierowanie) i exitzakończy działanie skryptu w normalny sposób. Zarówno returni exitmoż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ę returnjako równoważne, exitjeśli wywoływane jest poza funkcją lub skryptem kropkowym.

Zaktualizowano : To, co możesz zrobić we współczesnych wersjach, kshto sprawdzenie specjalnej zmiennej .sh.levelustawionej 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.

function issourced {
    [[ ${.sh.level} -eq 2 ]]
}

issourced && echo this script is sourced

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 kshfunkcji dyscypliny i pewnych sztuczek związanych z debugowaniem pułapki w celu emulacji FUNCNAMEtablicy bash .)

Kanoniczna odpowiedź tutaj: http://mywiki.wooledge.org/BashFAQ/109 oferuje również $-jako kolejny wskaźnik (choć niedoskonały) stanu powłoki.


Uwagi:

  • możliwe jest tworzenie funkcji bash o nazwie „main” i „source” ( przesłanianie wbudowanego ), nazwy te mogą pojawiać się wFUNCNAME[] ale dopóki testowany jest tylko ostatni element w tej tablicy, nie ma dwuznaczności.
  • Nie mam dobrej odpowiedzi na pdksh. Najbliższa rzecz, jaką mogę znaleźć, dotyczy tylko tego pdksh, 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ć ...
pan. spuratic
źródło
Co powiesz ${FUNCNAME[(( ${#FUNCNAME[@]} - 1 ))]}na zdobycie ostatniego (dolnego) przedmiotu ze stosu? Wtedy testowanie z „main” (negate for OP) było dla mnie najbardziej niezawodne.
Adrian Günter
Jeśli mam PROMPT_COMMANDzestaw, pojawia się on jako ostatni indeks FUNCNAMEtablicy, jeśli uruchomię source sourcetest.sh. Odwracanie czeku (szukanie mainjako ostatniego indeksu) wydaje się bardziej niezawodne:is_main() { [[ ${FUNCNAME[@]: -1} == "main" ]]; } .
dimo414
1
Strona man podaje, że FUNCNAMEjest dostępna tylko w funkcjach. Według moich testów z declare -p FUNCNAME, bashzachowuje się inaczej. v4.3 daje błąd poza funkcjami, podczas gdy v4.4 daje declare -a FUNCNAME. Zarówno (!) Powrót maindo ${FUNCNAME[0]}w głównym skrypcie (jeśli jest wykonywana), natomiast $FUNCNAMEnic nie daje. I: Jest tak wiele skryptów „ab” korzystających z $BASH_SOURCEfunkcji zewnętrznych, że wątpię, czy można to zmienić lub zmienić.
Tino
24

Uwaga edytora: Rozwiązanie tej odpowiedzi działa solidnie, ale jest bashtylko. Można to usprawnić
(return 2>/dev/null).

TL; DR

Spróbuj wykonać returninstrukcję. 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:

#!/usr/bin/env sh

# Try to execute a `return` statement,
# but do it in a sub-shell and catch the results.
# If this script isn't sourced, that will raise an error.
$(return >/dev/null 2>&1)

# What exit code did that give?
if [ "$?" -eq "0" ]
then
    echo "This script is sourced."
else
    echo "This script is not sourced."
fi

Wykonaj to bezpośrednio:

shell-prompt> sh test.sh
output: This script is not sourced.

Źródło:

shell-prompt> source test.sh
output: This script is sourced.

Dla mnie działa to w Zsh i Bash.

Wyjaśnienie

returnOś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:

shell-prompt> return
output: ...can only `return` from a function or sourced script

Nie musisz widzieć tego komunikatu o błędzie, więc możesz przekierować dane wyjściowe do dev / null:

shell-prompt> return >/dev/null 2>&1

Teraz sprawdź kod wyjścia. 0 oznacza OK (nie wystąpiły błędy), 1 oznacza błąd:

shell-prompt> echo $?
output: 1

Chcesz także wykonać returninstrukcję wewnątrz podpowłoki. Kiedy returninstrukcja 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ą$(...) :

shell-prompt> $(return >/dev/null 2>$1)

Teraz możesz zobaczyć kod wyjścia podpowłoki, który powinien wynosić 1, ponieważ w podpowłoce został zgłoszony błąd:

shell-prompt> echo $?
output: 1
użytkownik5754163
źródło
Nie udaje mi się to w wersji 0.5.8-2.1ubuntu2 $ readlink $(which sh) dash $ . test.sh This script is sourced. $ ./test.sh This script is sourced.
Phil Rutschman,
3
POSIX nie określa, co returnnależy zrobić na najwyższym poziomie ( pubs.opengroup.org/onlinepubs/9699919799/utilities/… ). W dashtraktuje płaszcza returnna górnym poziomie exit. Inne powłoki lubią bashlub zshnie zezwalają returnna najwyższym poziomie, co jest cechą takiej techniki.
user5754163
Działa w sh, jeśli usuniesz $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.
Tytułowy
@Eponymous: Ponieważ dashtam, gdzie to rozwiązanie nie działa, działa shna przykład w systemie Ubuntu, na przykład to rozwiązanie zazwyczaj nie działa sh. 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 $).
mklement0
2
returning bez wyrażenia zwracanego przez explcit, psuje się podczas pisania sourceskryptów tuż po źle wydanym poleceniu. Zaproponowano edycję rozszerzenia.
DimG
12

FWIW, po przeczytaniu wszystkich innych odpowiedzi, wymyśliłem dla mnie następujące rozwiązanie:

Aktualizacja: Właściwie ktoś zauważył poprawiony od czasu błąd w innej odpowiedzi, która również wpłynęła na moją. Myślę, że aktualizacja tutaj jest również ulepszeniem (zobacz zmiany, jeśli jesteś ciekawy).

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 poza mainfunkcją.

Zgodnie z poniższymi komentarzami, ta odpowiedź tutaj najwyraźniej nie działa dla wszystkich bashwariantów. Również nie dla systemów, na których /bin/shjest oparty bash. IE nie działa w wersji bash3.x na MacOS. (Obecnie nie wiem, jak to rozwiązać.)

#!/bin/bash

# Function definitions (API) and shell variables (constants) go here
# (This is what might be interesting for other shells, too.)

# this main() function is only meant to be meaningful for bash
main()
{
# The script's execution part goes here
}

BASH_SOURCE=".$0" # cannot be changed in bash
test ".$0" != ".$BASH_SOURCE" || main "$@"

Zamiast ostatnich 2 wierszy możesz użyć następującego (moim zdaniem mniej czytelnego) kodu, aby nie ustawiać się BASH_SOURCEw innych powłokach i pozwalać set -ena pracę w main:

if ( BASH_SOURCE=".$0" && exec test ".$0" != ".$BASH_SOURCE" ); then :; else main "$@"; fi

Ten przepis skryptu ma następujące właściwości:

  • Jeśli jest wykonywany bashnormalnym sposobem, mainjest wywoływany. Pamiętaj, że nie obejmuje to takiego połączenia bash -x script(gdzie scriptnie zawiera ścieżki), patrz poniżej.

  • Jeśli pochodzi z bash, mainjest wywoływany tylko wtedy, gdy skrypt wywołujący ma taką samą nazwę. (Na przykład, jeśli sam się pozyskuje lub przez bash -c 'someotherscript "$@"' main-script args..gdzie main-scriptmusi być, co testwidzi jako $BASH_SOURCE).

  • Jeśli jest pozyskiwany / wykonywany / czytany / evaledytowany przez powłokę inną niż bash, mainnie jest wywoływany ( BASH_SOURCEzawsze jest inny niż $0).

  • mainnie jest wywoływany, jeśli bashczyta skrypt ze standardowego wejścia, chyba że ustawisz $0pusty ciąg znaków tak:( exec -a '' /bin/bash ) <script

  • Wywoływane przez bashprzy pomocy eval ( eval "`cat script`" wszystkie cytaty są ważne! ) Z innego skryptu main. Jeśli evaljest uruchamiany bezpośrednio z wiersza poleceń, jest to podobne do poprzedniego przypadku, w którym skrypt jest odczytywany ze standardowego wejścia. ( BASH_SOURCEjest puste, choć $0zwykle jest /bin/bashzmuszone do czegoś zupełnie innego).

  • Jeśli mainnie jest wywołany, zwraca return true( $?=0).

  • Nie opiera się to na nieoczekiwanym zachowaniu (wcześniej pisałem nieudokumentowane, ale nie znalazłem żadnej dokumentacji, której nie można unsetani zmienić BASH_SOURCE):

    • BASH_SOURCEjest zarezerwowaną tablicą bash . Ale pozwolenie BASH_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 wersjach bash).
    • Nie ma dokumentacji, która BASH_SOURCEdział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 z bashwersją v4.3 i v4.4, niestety nie mam już wersji bashv3.x) i że zbyt wiele skryptów ulegnie awarii, jeśli $BASH_SOURCEprzestanie działać zgodnie z obserwacjami. Dlatego oczekuję, że BASH_SOURCEpozostanie tak samo, jak w przypadku przyszłych wersji bash.
    • W przeciwieństwie (fajne znalezisko, BTW!) Zastanów się ( return 0 ), co daje, 0jeśli pochodzą, a 1jeśli nie pochodzą. Jest to trochę nieoczekiwane nie tylko dla mnie i (zgodnie z tamtymi odczytami) POSIX mówi, że returnz podpowłoki jest niezdefiniowane zachowanie (a returntutaj 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łe bashwersje przypadkowo zmienią zachowanie powrotu w tym przypadku.
  • Niestety bash -x script 1 2 3nie działa main. (Porównaj, script 1 2 3gdzie scriptnie 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
    • To bash script 1 2 3nie działa mainmożna uznać za funkcję.
  • Zauważ, że ( exec -a none script )wywołania main( bashnie przekazują go $0do skryptu, w tym celu musisz użyć, -cjak pokazano w ostatnim punkcie).

Tak więc, z wyjątkiem niektórych przypadków narożnych, mainjest 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.

Zauważ, że jest bardzo podobny do kodu Pythona:

if __name__ == '__main__': main()

Co również zapobiega wywoływaniu main, z wyjątkiem niektórych przypadków narożnych, ponieważ można zaimportować / załadować skrypt i wymusić to__name__='__main__'

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 sourceingerencji, 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)
  • Jeśli potokujesz go do skorupy jak w curl https://example.com/script | $SHELL
  • (Uwaga: jest to prawdą tylko wtedy, gdy twój bashjest 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ć mainrę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

Tino
źródło
Testowane na ksh i bash-4.3. Miły. Szkoda, że ​​twoja odpowiedź będzie ciężka, biorąc pod uwagę, że inne odpowiedzi miały już lata zbierania głosów.
hagello
dziękuję za tę odpowiedź. Doceniam dłuższy, „mniej czytelny” test z instrukcją IF, ponieważ miło jest poradzić sobie w obu sytuacjach, aby co najmniej doprowadzić do cichej awarii. W moim przypadku potrzebuję skryptu, aby uzyskać źródło lub w inny sposób poinformować użytkownika o jego błędzie w nieużywaniu źródła.
Tim Richardson,
@Tino: Jeśli chodzi o „mogą być również pozyskiwane przez różne powłoki”: na macOS, gdzie /bin/sh jest efektywnie bashw trybie POSIX, przypisywanie do BASH_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 (np zsh <your-script>uczyni skrypt błędnie sądzi, że jest pozyskiwany ). (Już wspomnieć, że rurociągi usterki kodu we wszystkich muszli.)
mklement0
@Tino: Na marginesie: Chociaż . <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.
mklement0
1
@ mklement0 Jeszcze raz dziękuję, dodałem notatkę, że jest problem. Dla innych czytelników: Gdy pochodzi z bash v3.x, nie powinien się uruchamiać main, ale robi to w tym przypadku! A kiedy pochodzą z tego /bin/sh, co bash --posixdzieje się w tym przypadku, dzieje się tak samo, i to również jest zupełnie złe.
Tino
6

Działa to później w skrypcie i nie zależy od zmiennej _:

## Check to make sure it is not sourced:
Prog=myscript.sh
if [ $(basename $0) = $Prog ]; then
   exit 1  # not sourced
fi

lub

[ $(basename $0) = $Prog ] && exit
Jim Mcnamara
źródło
1
Myślę, że ta odpowiedź jest jedną z niewielu zgodnych z POSIX tutaj. Oczywistym minusem jest to, że musisz znać nazwę pliku i nie działa, jeśli oba skrypty mają tę samą nazwę pliku.
JepZ
5

Dam odpowiedź specyficzną dla BASH. Przepraszam, Korn Shell. Załóżmy, że twoja nazwa skryptu to include2.sh; następnie utworzyć funkcję wewnątrzinclude2.sh nazywa am_I_sourced. Oto moja wersja demonstracyjna include2.sh:

am_I_sourced()
{
  if [ "${FUNCNAME[1]}" = source ]; then
    if [ "$1" = -v ]; then
      echo "I am being sourced, this filename is ${BASH_SOURCE[0]} and my caller script/shell name was $0"
    fi
    return 0
  else
    if [ "$1" = -v ]; then
      echo "I am not being sourced, my script/shell name was $0"
    fi
    return 1
  fi
}

if am_I_sourced -v; then
  echo "Do something with sourced script"
else
  echo "Do something with executed script"
fi

Teraz spróbuj wykonać to na wiele sposobów:

~/toys/bash $ chmod a+x include2.sh

~/toys/bash $ ./include2.sh 
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script

~/toys/bash $ bash ./include2.sh 
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script

~/toys/bash $ . include2.sh
I am being sourced, this filename is include2.sh and my caller script/shell name was bash
Do something with sourced script

To działa bez wyjątku i nie używa kruchych $_rzeczy. Ta sztuczka wykorzystuje funkcję introspekcji BASH, tj. Wbudowane zmienne FUNCNAMEi BASH_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.

Wirawan Purwanto
źródło
1
Wyjaśnienie: Ta funkcja wymaga BASH w wersji 3+ do działania. W BASH 2 FUNCNAME jest zmienną skalarną zamiast tablicy. Również BASH 2 nie ma zmiennej tablicowej BASH_SOURCE.
Wirawan Purwanto
4

Chciałbym zasugerować niewielką korektę bardzo pomocnej odpowiedzi Dennisa , aby uczynić ją nieco bardziej przenośną, mam nadzieję:

[ "$_" != "$0" ] && echo "Script is being sourced" || echo "Script is a subshell"

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.

użytkownik354193
źródło
2

$_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 $0tego w następujący sposób:

name="myscript.sh"

main()
{
    echo "Script was executed, running main..."
}

case "$0" in *$name)
    main "$@"
    ;;
esac

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 functionargzeroswój .zshenv.

Mikel
źródło
1

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:

/bin/ksh -c ./myscript.sh

(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ć:

/bin/ksh ./myscript.sh

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

SOURCED=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
    [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && SOURCED=1
elif [ -n "$KSH_VERSION" ]; then
    [[ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ]] && SOURCED=1
elif [ -n "$BASH_VERSION" ]; then
    [[ $0 != "$BASH_SOURCE" ]] && SOURCED=1
elif grep -q dash /proc/$$/cmdline; then
    case $0 in *dash*) SOURCED=1 ;; esac
fi

Dodaj obsługę egzotycznych muszli :)

Stéphane Desneux
źródło
W ksh 93+u, ksh ./myscript.shdziała dobrze dla mnie (z moim oświadczeniem) - co w wersji używasz?
mklement0
Obawiam się, że nie ma sposobu, aby wiarygodnie ustalić, czy skrypt jest pozyskiwany przy użyciu tylko funkcji POSIX: twoja próba zakłada Linux ( /proc/$$/cmdline) i koncentruje się dashtylko na (który działa również shna przykład w Ubuntu). Jeśli chcesz poczynić pewne założenia, możesz zbadać pod $0kątem rozsądnego - ale niepełnego - testu, który jest przenośny.
mklement0
++, jeśli chodzi o podstawowe podejście - skorzystałem z możliwości dostosowania go do tego, co uważam za najlepsze przenośne przybliżenie wsparcia sh/ dashrównież w dodatku do mojej odpowiedzi.
mklement0
0

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ą callerwyjścia, ale nie sądzę, że istnieje odpowiednik w ksh.

Michal Čihař
źródło
$0działa bash, ksh93i pdksh. Nie muszę ksh88testować.
Mikel
0

Potrzebowałem jednowierszowego, który działa na [mac, linux] z bash.version> = 3 i żadna z tych odpowiedzi nie pasuje do rachunku.

[[ ${BASH_SOURCE[0]} = $0 ]] && main "$@"
Karsten
źródło
1
bashRozwiązanie działa dobrze (można uprościć do $BASH_SOURCE), ale kshrozwiązanie to nie jest solidna: Jeśli skrypt jest wchłaniany przez innego skryptu , dostaniesz fałszywie dodatni.
mklement0
0

Od razu do rzeczy: musisz ocenić, czy zmienna „$ 0” jest równa nazwie twojej powłoki.


Lubię to:

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
    echo "The script was sourced."
else
    echo "The script WAS NOT sourced."
fi


Przez SHELL :

$ bash check_source.sh 
First Parameter: check_source.sh

The script WAS NOT sourced.

Przez ŹRÓDŁO :

$ source check_source.sh
First Parameter: bash

The script was sourced.



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ś:

  • rozszerz możliwości swojego
  • używając switch / case, jeśli chcesz.

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 .

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" || "$0" == "-bash" ]] ; then
    echo "The script was sourced."
else
    echo "The script WAS NOT sourced."
fi

LUB

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
    echo "The script was sourced."
elif [[ "$0" == "-bash" ]] ; then
    echo "The script was sourced via SSH session."
else
    echo "The script WAS NOT sourced."
fi
ivanleoncz
źródło
2
Zagłosowano, ponieważ jest to po prostu złe: /bin/bash -c '. ./check_source.sh'daje The script WAS NOT sourced.. Ten sam błąd: ln -s /bin/bash pumuckl; ./pumuckl -c '. ./check_source.sh'->The script WAS NOT sourced.
Tino
2
Twoje zdanie zmieniło cały scenariusz i wniosło wielki wkład, Tino. Dzięki!
ivanleoncz
0

Skończyło się na sprawdzeniu [[ $_ == "$(type -p "$0")" ]]

if [[ $_ == "$(type -p "$0")" ]]; then
    echo I am invoked from a sub shell
else
    echo I am invoked from a source command
fi

Kiedy używasz curl ... | bash -s -- ARGSdo uruchamiania zdalnego skryptu w locie, 0 $ będzie po prostu bashzamiast normalnego /bin/bashpo uruchomieniu rzeczywistego pliku skryptu, więc używamtype -p "$0" do pokazania pełnej ścieżki bash.

test:

curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath | bash -s -- /a/b/c/d/e /a/b/CC/DD/EE

source <(curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath)
relpath /a/b/c/d/e /a/b/CC/DD/EE

wget https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath
chmod +x relpath
./relpath /a/b/c/d/e /a/b/CC/DD/EE
osexp2003
źródło
0

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:

# NOTE: This script may be used as a standalone executable, or callable library.
# To source this script, add the following *prior* to including it:
# export ENTRY_POINT="$0"

main()
{
    echo "Running in direct executable context!"
}

if [ -z "${ENTRY_POINT}" ]; then main "$@"; fi

Uwaga: używam exporttylko upewnij się, że ten mechanizm można rozszerzyć na podprocesy.

BuvinJ
źródło