Czy w końcu jest różnica między „.” A „source” w bash?

37

Szukałem różnicy między „.” oraz wbudowane polecenia „source” i kilka źródeł (np. w tej dyskusji i na stronie podręcznika bash ) sugerują, że są one takie same.

Jednak po wystąpieniu problemu ze zmiennymi środowiskowymi przeprowadziłem test. Utworzyłem plik, testenv.shktóry zawiera:

#!/bin/bash
echo $MY_VAR

W wierszu polecenia wykonałem następujące czynności:

> chmod +x testenv.sh
> MY_VAR=12345
> ./testenv.sh

> source testenv.sh
12345
> MY_VAR=12345 ./testenv.sh
12345

[zauważ, że 1. formularz zwrócił pusty ciąg]

Tak, ten mały eksperyment sugeruje, że jest różnica po wszystkim, gdzie za pomocą polecenia „source”, dziecko środowisko dziedziczy wszystkie zmienne z jednym rodzicem, gdzie po „” to nie.

Czy coś mi brakuje, czy jest to nieudokumentowana / przestarzała funkcja bash ?

[GNU bash, wersja 4.1.5 (1) -release (x86_64-pc-linux-gnu)]

tak
źródło

Odpowiedzi:

68

Krótka odpowiedź

W twoim pytaniu drugie polecenie nie używa ani .wbudowanej powłoki, ani sourcewbudowanej. Zamiast tego faktycznie uruchamiasz skrypt w osobnej powłoce, wywołując go po imieniu, tak jak w przypadku każdego innego pliku wykonywalnego. Daje to oddzielny zestaw zmiennych (chociaż jeśli eksportujesz zmienną w jej powłoce nadrzędnej, będzie to zmienna środowiskowa dla dowolnego procesu potomnego , a zatem zostanie uwzględniona w zmiennych podrzędnej powłoki ). Jeśli zmienisz na /spację, uruchomiłbyś go z .wbudowanym odpowiednikiem source.

Rozszerzone objaśnienie

Oto składnia sourcewbudowanej powłoki, która wykonuje zawartość skryptu w bieżącej powłoce (a tym samym ze zmiennymi bieżącej powłoki):

source testenv.sh

To jest składnia .wbudowanego, który robi to samo, co source:

. testenv.sh

Jednak ta składnia uruchamia skrypt jako plik wykonywalny, uruchamiając nową powłokę, aby go uruchomić:

./testenv.sh

To nie używa .wbudowanego. Jest raczej .częścią ścieżki do wykonywanego pliku. Ogólnie mówiąc, możesz uruchomić dowolny plik wykonywalny w powłoce, wywołując go z nazwą zawierającą co najmniej jeden /znak. ./Najłatwiejszym sposobem jest uruchomienie pliku w bieżącym katalogu . Jeśli bieżący katalog nie znajduje się w twoim PATH, nie możesz uruchomić skryptu za pomocą polecenia testenv.sh. Zapobiega to przypadkowemu uruchomieniu plików w bieżącym katalogu, gdy zamierzają wykonać polecenie systemowe lub inny plik, który istnieje w katalogu wymienionym w PATHzmiennej środowiskowej.

Ponieważ uruchomienie pliku według nazwy (zamiast z sourcelub .) uruchamia go w nowej powłoce, będzie on miał swój własny zestaw zmiennych powłoki. Nowa powłoka dziedziczy zmienne środowiskowe z procesu wywołującego (którym w tym przypadku jest powłoka interaktywna), a te zmienne środowiskowe stają się zmiennymi powłoki w nowej powłoce. Jednak, aby zmienna powłoki została przekazana do nowej powłoki, musi mieć miejsce jedna z następujących sytuacji:

  1. Zmienna powłoki została wyeksportowana, przez co jest zmienną środowiskową. Użyj do tego exportwbudowanej powłoki. W twoim przykładzie możesz użyć, export MY_VAR=12345aby ustawić i wyeksportować zmienną w jednym kroku, lub jeśli jest już ustawiona, możesz po prostu użyć export MY_VAR.

  2. Zmienna powłoki jest jawnie ustawiona i przekazywana dla uruchomionego polecenia, co powoduje, że jest zmienną środowiskową na czas wykonywania polecenia. To zwykle realizuje że:

    MY_VAR=12345 ./testenv.sh

    Jeśli MY_VARjest to zmienna powłoki, która nie została wyeksportowana, możesz nawet uruchomić testenv.shMY_VARjako zmienną środowiskową, ustawiając ją na sobie :

    MY_VAR="$MY_VAR" ./testenv.sh

./ Składnia skryptów wymaga do działania wiersza skrótu (poprawnie)

Nawiasem mówiąc, należy pamiętać, że przy wywołaniu pliku wykonywalnego według nazwy jak wyżej (a nie z .lub sourceShell Zabudowy), co Shell program jest używany do uruchamiania jest nie zwykle określana przez co powłoka używasz go od . Zamiast:

  • W przypadku plików binarnych jądro może być skonfigurowane do uruchamiania plików określonego typu. Sprawdza dwa pierwsze bajty pliku pod kątem „magicznej liczby”, która wskazuje, jaki to binarny plik wykonywalny. W ten sposób można uruchamiać binarne pliki wykonywalne.

    Jest to oczywiście niezwykle ważne, ponieważ skrypt nie może działać bez powłoki lub innego interpretera, który jest wykonywalnym plikiem binarnym! Ponadto wiele poleceń i aplikacji to skompilowane pliki binarne, a nie skrypty.

    ( #!to tekstowa reprezentacja „magicznej liczby” wskazującej tekst wykonywalny).

  • W przypadku plików, które powinny działać w powłoce lub innym interpretowanym języku, pierwszy wiersz wygląda następująco:

    #!/bin/sh

    /bin/shmoże być zastąpiony przez dowolną inną powłokę lub interpreter przeznaczony do uruchomienia programu. Na przykład program w języku Python może zaczynać się od wiersza:

    #!/usr/bin/python

    Linie te nazywane są hashbang, shebang i wieloma innymi podobnymi nazwami. Zobacz ten wpis FOLDOC , ten artykuł w Wikipedii i Czy tłumacz! # / Bin / sh jest odczytywany? po więcej informacji.

  • Jeśli plik tekstowy jest oznaczony jako wykonywalny i uruchamiasz go ze swojej powłoki (jak ./filename), ale nie zaczyna się od #!, jądro nie wykonuje go. Jednak widząc, że tak się stało, twoja powłoka spróbuje ją uruchomić, przekazując swoją nazwę do jakiejś powłoki. Istnieje niewiele wymagań dotyczących tego, co to jest powłoka ( „powłoka wykona polecenie równoważne z wywołaniem powłoki ...” ). W praktyce niektóre powłoki - w tym bash* - uruchamiają inne wystąpienia, podczas gdy inne używają/bin/sh. Gorąco polecam, aby tego uniknąć i zamiast tego użyć linii hashbang (lub uruchomić skrypt, przekazując go do pożądanego interpretera, np bash filename.).

    * Podręcznik GNU Bash , 3.7.2 Wyszukiwanie i wykonywanie poleceń : „Jeśli wykonanie się nie powiedzie, ponieważ plik nie jest w formacie wykonywalnym, a plik nie jest katalogiem, zakłada się, że jest to skrypt powłoki i powłoka wykonuje go zgodnie z opisem w skryptach powłoki ”.

Eliah Kagan
źródło
2
Przydało mi się sourceto, że funkcje stały się dostępne z bash bez potrzeby ładowania lub ponownego uruchamiania. Przykład #!/bin/bash function olakease {echo olakease;}. Po załadowaniu source file.shmożesz bezpośrednio zadzwonić olakeasez bash. Naprawdę to lubię. Źródło wykonuje następnie ładuje wiele rzeczy, kropka .służy tylko do wykonania i jest jak użyciebash file.sh
erm3nda
2
@ erm3nda .ma również takie zachowanie: . file.shi source file.shrób dokładnie to samo, włączając w to zachowanie funkcji zdefiniowanych w file.sh. (Być może myślisz o tym ./file.sh, co jest inne. Ale to nie korzysta z .wbudowanego; zamiast tego .jest częścią ścieżki.)
Eliah Kagan
Och !, nie przeczytałem dokładnie pliku. [Spacja]. Dziękuję bardzo mach.
erm3nda
13

Tak, czegoś brakuje.

Myślę, że mylisz „.” oznacza to bieżący katalog, jak w „ ./testenv.shi”. to znaczy source(które jest wbudowanym poleceniem). Tak więc w przypadku, gdy „.” oznacza, sourceże byłoby . ./testenv.sh. Ma sens?

Spróbuj tego:

MY_VAR=12345 
. ./testenv.sh
użytkownik1477
źródło
2
./Informuje go dokładnie tam, gdzie plik to bez niego, bash będzie wyglądać ścieżka pierwsza, a następnie spróbuj bieżący dir jeśli go nie znajdzie. Jeśli bash działa w trybie POSIX i nie podasz ścieżki do pliku (np. ./), Przeszuka on tylko PATH i nie znajdzie pliku, jeśli bieżący katalog nie znajduje się w PATH.
geirha
@geirha Tak, masz rację, source(i .) faktycznie najpierw sprawdzi $ PATH, nawet jeśli tak naprawdę nie uruchamiają skryptu w zwykłym sensie. Mój (poprzedni) komentarz był niepoprawny.
Eliah Kagan
Krótko i do rzeczy +1
David Morales,