Zmienne środowiskowe

Skoro już wspomniałem o zmiennej PATH zajmijmy się dokładniej zmiennymi środowiskowymi. Pierwszą sprawą jest zrozumienie czym są zmienne środowiskowe. Otóż są to obszary pamięci przechowujące dane. Obszary te identyfikowane są przez nazwy. Przyjęło się, że nazwy zmiennych środowiskowych zapisujemy wielkimi literami w odróżnieniu od zmiennych wykorzystywanych lokalnie w skryptach. Wartości przechowywane w zmiennych środowiskowych to informacje istotne dla funkcjonowania systemu lub poszczególnych aplikacji. Część tych zmiennych ustawiana jest przy starcie systemu w trakcie wykonywania skryptów startowych z katalogu /etc/rc.d. Większość zmiennych ustawiana jest w sposób specyficzny dla użytkownika w trakcie procesu logowania. Po poprawnej autoryzacji następuje uruchomienie powłoki w trybie logowania. W trakcie tego uruchomienia wykonywane są pliki /etc/profile i /etc/bashrc oraz pliki z katalogu domowego użytkownika .bash_profile i .bashrc. Wymienione pliki są tak naprawdę skryptami powłoki zawierającymi polecenia ustawiające odpowiednie wartości zmiennych środowiskowych. Również niektóre aplikacje powodują ustawienie zmiennych środowiskowych. Zaklęciem takim jest polecenie su wykorzystywane do chwilowej zmiany tożsamości (np. przelogowanie się na roota). Aplikacja ta ustawia zmienne USER, LOGNAME, SHELL i HOME według informacji odpowiednich dla danego użytkownika odczytanych z pliku /etc/passwd.

Wiemy już czym są zmienne środowiskowe i gdzie są ustawiane. Zajmiemy się teraz wykorzystaniem tych informacji. Jak zwykle zaczynamy od zapoznania się z tym, co już jest dostępne. Listę zmiennych środowiskowych wraz z ich wartościami możemy obejrzeć wykorzystując dwa polecenia env oraz set. Oba polecenia podane bez parametrów wyświetlają listę zmiennych. Zapewne zauważyłeś, że lista zmiennych podawanych przez polecenie set jest dłuższa. Wynika to z faktu, że polecenie set pokazuje wszystkie zmienne obowiązujące w aktualnym środowisku, natomiast polecenie env tylko te, które są dziedziczone przez środowisko potomne (są przekazywane do środowiska potomnego). Co z tego wynika? Całkiem sporo. Jeśli zmienna jest dziedziczona przez środowisko potomne, to znaczy, że wszystkie skrypty i aplikacje będą znały wartość tej zmiennej i będą z niej mogły korzystać. Korzystać to nie znaczy zmieniać. Zmiana wartości zmiennej środowiskowej w procesie potomnym nie ma wpływu na jej wartość na poziomie środowiska rodzica. Za moment prześledzimy te właściwości na przykładach, ale wcześniej jeszcze jedno wyjaśnienie. Zapewne zastanawiasz się czym różnią się zmienne dziedziczone od tych niedziedziczonych. Już wyjaśniam. Aby dana zmienna mogła być dziedziczona musi zostać wyeksportowana. Wykonujemy to poleceniem basha export ZMIENNA. Od momentu wyeksportowania zmienna będzie dostępna we wszystkich procesach potomnych naszego środowiska, ale nie w procesach nadrzędnych.

Zajmijmy się zatem praktycznym wykorzystaniem zdobytej wiedzy. Częstym problemem, z którym borykają się początkujący adepci wiedzy tajemnej jest brak ``dostępu'' do zaklęcia. Oto przykład. Zgłosiłeś na forum dyskusyjnym pewien problem i zostałeś poproszony o podanie wyniku polecenia ifconfig -a (wyświetla informację o konfiguracji interfejsów sieciowych), więc wykonujesz i ...:

[tuptus@dreptak tuptus]$ ifconfig -a
bash: ifconfig: command not found
[tuptus@dreptak tuptus]$
Czyżby w naszym systemie nie było tak podstawowego zaklęcia? Sprawdźmy to wykorzystując polecenie whereis :
[tuptus@dreptak tuptus]$ whereis ifconfig
ifconfig: /sbin/ifconfig /usr/share/man/man8/ifconfig.8.gz
[tuptus@dreptak tuptus]$
Okazuje się, że jest i jest nawet manual do niego. Dlaczego zatem shell poinformował, że nie może go znaleźć? Przyczyna tkwi w ustawieniu zmiennej środowiskowej PATH. Zmienna ta jest ustawiana w procesie logowania w ramach wykonania pliku /etc/profile i przechowuje listę katalogów, które shell będzie przeszukiwał w celu znalezienia podanego przez nas zaklęcia. Sprawdźmy jak jest ona aktualnie ustawiona :
[tuptus@dreptak tuptus]$ echo $PATH
/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/tuptus/bin
Faktycznie, katalogu /sbin nie ma w zmiennej PATH. W wiekszości przypadków zwykli użytkownicy nie potrzebują dostępu do katalogów sbin (w systemie jest kilka takich), ale ty nie jesteś zwykłym użytkownikiem tylko adeptem wiedzy wszelakiej.

Uwaga.
Zwróć uwagę, że w celu odczytania wartości zmiennej przed jej nazwą stawiamy znak $. W shellu jest jeszcze kilka innych możliwości odwoływania się do wartości zmiennej. Jeśli w przykładowych skryptach będziemy je wykorzystywać, to wyjaśnię ich znaczenie. W tej chwili dociekliwych czytelników odsyłam do dokumentacji basha.
Wniosek z powyższych rozważań nasuwa się sam. Zmienną PATH należy zmodyfikować. Zaczniemy modyfikacje środowiska od drobnych kroczków :
[tuptus@dreptak tuptus]$ PATH=/sbin:$PATH
[tuptus@dreptak tuptus]$ ifconfig -a
eth0      Link encap:Ethernet  HWaddr 00:50:04:67:CC:93
          inet addr:192.168.1.1  Bcast:192.168.1.255  Mask:255.255.255.0
(...)
[tuptus@dreptak tuptus]$
Jak widzisz polecenie zadziałało. Możesz jeszcze sprawdzić jaką obecnie zawartość ma zmienna PATH. Oczywiście nowa wartość tej zmiennej będzie widziana również w procesach potomnych, gdyż już wcześniej uzyskała ona właściwość dziedziczenia. Możemy to sprawdzić pisząc polecenie bash -c 'echo $PATH'.

Pozostaje jeszcze jeden problem do rozwiązania. Po ponownym zalogowaniu zmienna będzie miała ponownie wartość początkową. Oczywiście do modyfikacji pliku /etc/profile nie mamy uprawnień, a jeśli nawet znasz hasło roota, to odradzam modyfikacje wspomnianego pliku, gdyż będzie to miało wpływ na wszystkich użytkowników systemu. Tego typu modyfikacje należy wprowadzać do pliku .bash_profile znajdującego się w katalogu domowym. W pliku tym już jest linia modyfikująca ścieżki dostępu -- dopisz na końcu potrzebne Ci katalogi.

Teraz sprawdźmy co się będzie działo gdy zmienną tą ustawimy w procesie potomnym. Przejdź do drugiego terminala (AltCtrlF2 w środowisku tekstowym lub otwórz nowe okno terminala w środowisku graficznym) i sprawdź aktualną wartość zmiennej PATH. Teraz uruchom powłokę potomną, zmień wartość zmiennej, sprawdź jej wartość i wróć do poprzedniej powłoki.

[tuptus@dreptak tuptus]$ echo $PATH
/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/tuptus/bin
[tuptus@dreptak tuptus]$ bash
[tuptus@dreptak tuptus]$ PATH=/sbin:$PATH
[tuptus@dreptak tuptus]$ echo $PATH
/sbin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/tuptus/bin
[tuptus@dreptak tuptus]$ exit
[tuptus@dreptak tuptus]$ echo $PATH
/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/tuptus/bin
Polecenie bash uruchamia powłokę potomną. Jak widać wartość zmiennej PATH została zmieniona w powłoce potomnej, ale po powrocie do powłoki rodzimej jej wartość ponownie jest taka, jak przed zmianą. Ten przykład obrazuje to, o czym wcześniej wspominałem, zmiany eksportowanej zmiennej dotyczą tylko procesów potomnych i nie mają wpływu na powłokę ``rodzica''.

Zajmiemy się teraz tematem eksportowania zmiennych środowiskowych. Omówimy go na przykładzie zmiennej EDITOR. Zmienna ta odczytywana jest przez wiele aplikacji w celu określenia domyślnego edytora wykorzystywanego przez te aplikacje. Doskonałym przykładem jej wykorzystania są demony zegarowe (crontab i at) omówione w dalszej części kursu. I znowu zaczniemy od sprawdzenia co nam oferuje system :

[tuptus@dreptak tuptus]$ echo $EDITOR

[tuptus@dreptak tuptus]$
Wygląda na to, że nie oferuje nam niczego ciekawego. Wypełnijmy zatem tę lukę :
[tuptus@dreptak tuptus]$ EDITOR=/usr/bin/vim
[tuptus@dreptak tuptus]$ echo $EDITOR
/usr/bin/vim
[tuptus@dreptak tuptus]$
Wygląda to całkiem obiecująco. Mamy ustawioną zmienną środowiskową, tylko czy aplikacje będą umiały z niej skorzystać? Sprawdźmy co zobaczy proces potomny :
[tuptus@dreptak tuptus]$ bash -c 'echo $EDITOR'

[tuptus@dreptak tuptus]$
I mamy problem. Okazuje się bowiem, że proces potomny (w tym przypadku polecenie echo) nie zobaczy nic ciekawego. Przyczyną jest brak możliwości dziedziczenia zmiennej, która nie została wyeksportowana. Zobaczmy zatem jak się zachowa echo po dokonaniu exportu :
[tuptus@dreptak tuptus]$ export EDITOR
[tuptus@dreptak tuptus]$ bash -c 'echo $EDITOR'
/usr/bin/vim
[tuptus@dreptak tuptus]$
Pięknie. O to właśnie nam chodziło. Wyeksportowanie zmiennej pozwala procesowi potomnemu na odziedziczenie wartości tej zmiennej i może on z niej skorzystać. Teraz pozostaje jedynie dopisać polecenia do odpowiedniego pliku i już zawsze powłoka będzie wiedziała, że naszym ulubionym edytorem jest vim.

Tutaj zatrzymaj się na chwilkę. Wiemy już całkiem sporo o zmiennych środowiskowych, ale mamy kolejny dylemat. Skoro proces potomny nie może zmieniać wartości zmiennych obowiązujących w środowisku rodzicielskim, to w jaki sposób skrypty ``startowe'' ustawiają te zmienne? W większości przypadków skrypty shella rozpoczynają się w poniższy sposób :

#!/bin/sh
# Tutaj jakiś komentarz

polecenie1
polecenia2
(...)
i mają ustawione bity x umożliwiające ich wykonanie. Wywołanie takiego skryptu powoduje uruchomienie interpretera /bin/sh jako procesu potomnego powłoki i wykonanie kolejnych poleceń zawartych w pliku. Zauważ jednak, że pliki .bash_profile i .bashrc nie mają tej pierwszej linii i nie mają ustawionych bitów x. Jak zatem są wykonywane? Oto rozwiązanie :
[tuptus@dreptak tuptus]$ . .bashrc
[tuptus@dreptak tuptus]$
Rozwiązaniem jest ta początkowa kropka. Wyobraź sobie, że to również jest zaklęcie. Należy je rozumieć jako polecenie ``w ramach bieżącej powłoki wykonaj polecenia znajdujące się w pliku .bashrc''. Tak więc nie jest tworzony żaden proces potomny, a plik nie musi mieć ustawionych bitów x, gdyż zawiera on jedynie listę poleceń do wykonania, a faktycznym procesem wykonującym te polecenia jest bieżący shell. W ten właśnie sposób działa proces logowania i tak samo Ty możesz uruchamiać swoje skrypty modyfikujące zmienne środowiskowe. Oczywiście modyfikacje zmiennych wcześniej wyeksportowanych nie wymagają takich zabiegów, co pokazałem wcześniej.

Tuptus 2006-05-05