===== Podstawy zmiennych środowiskowych ===== ==== Czym są ==== Zmienne środowiskowe ([[https://en.wikipedia.org/wiki/Environment_variable|Environment variables]]) to powiązany z procesem zbiór par nazwa – wartość. Nazwy zmiennych nie mogą zawierać znaku ''='' i powinny zawierać tylko litery (z zakresu ASCII), cyfry i podkreślnik, oraz powinny zaczynać się od litery. \\ Częstym stylem jest używanie tylko znaków ''A-Z'', ''_'' i ''0-9'' w nazwach zmiennych. **Każdy proces ma osobny zbiór zmiennych środowiskowych.** \\ Tworząc nowy proces można wybrać czy kopiuje wszystkie zmienne środowiskowe rodzica, czy kopiuje inny podany zbiór zmiennych. \\ Powłoka dzieli swoje zmienne środowiskowe na te które zostaną przekazane procesom potomnym (//eksportowane//) i te, które nie będą przekazane procesom potomnym. **Zmienne środowiskowe __nie__ są pomysłem powłoki, tylko systemu operacyjnego.** ==== Ustawianie ==== Ustawienie wartości zmiennej (i stworzenie jej, jeśli wcześniej nie istniała): \\ **''//NAZWA//=//wartość//''** \\ //Uwaga//: ''NAZWA =wartość'' spróbuje uruchomić program ''NAZWA'' z argumentem ''=wartość''. \\ //Uwaga//: ''NAZWA = wartość'' spróbuje uruchomić program ''NAZWA'' z argumentami ''='' oraz ''wartość''. \\ //Uwaga//: ''NAZWA= wartość'' spróbuje uruchomić program ''wartość'' przekazując mu wśród zmiennych środowiskowych zmienną ''NAZWA'' z pustą wartością. Do usunięcia zmiennej należy wykorzystać polecenie ''unset //NAZWA//''. \\ Uwaga: zwykle w powłoce pusta zmienna i nieustawiona zmienna zachowują się identycznie Domyślnie nowo tworzone zmienne nie są przekazywane do procesów potomnych. Aby ustawić przekazywanie zmiennej środowiskowej o nazwie ''//NAZWA//'' do procesów potomnych, należy wykonać: \\ **''export //NAZWA//''** \\ lub, aby jednocześnie ustawić przekazywanie do procesów potomnych i wartość: \\ **''export NAZWA=wartość''** \\ Uwaga: można wyeksportować jeszcze nieistniejącą zmienną i później ją stworzyć. POSIX nie przewiduje cofnięcia eksportu zmiennej; można ją co najwyżej usunąć komendą ''unset //NAZWA//'' i ustawić na nowo jako zwykłą nieeksportowaną zmienną. Bash pozwala na cofnięcie eksportowania zmiennej przez ''export -n //NAZWA//''. Aby zobaczyć listę wszystkich zmiennych środowiskowych, należy wykonać komendę ''**set**''((''set'' służy do wielu rzeczy naraz; z argumentami zmienia zachowanie powłoki.)). \\ Aby zobaczyć listę eksportowanych zmiennych środowiskowych, należy wykonać komendę ''**env**''((''env'' służy do uruchamiania procesów ze zmienioną listą zmiennych środowiskowych, natomiast bez argumentów wypisuje listę eksportowanych zmiennych.)). ==== (Najprostsze) odczytywanie wartości ==== Aby powłoka wstawiła w podane miejsce wartość zmiennej o nazwie ''//NAZWA//'', należy wpisać **''$//NAZWA//''**. \\ Np: jeśli wartość zmiennej ''$UID'' to 1000, to ''cd /run/user/$UID/'' wejdzie do katalogu ''/run/user/1000/'' a polecenie ''echo $NAZWA'' wyświetli wartość zmiennej ''NAZWA''. ++++ Przykłady | {{page>so:variables_substitutions:env_var_basics&inline}} ++++ ==== Wybrane standardowe zmienne ==== |''PATH''|Lista oddzielonych dwukropkami ścieżek w których będą szukane programy \\ Np. żeby wykonać komendę ''ls'', powłoka szuka pliku o nazwie ''ls'' w ścieżkach z ''PATH''| |''HOME''|Katalog domowy bieżącego użytkownika| |''PS1'' \\ ''PS2''|Podstawowy [[https://pl.wikipedia.org/wiki/Znak_zach%C4%99ty|znak zachęty]] (//prompt//) \\ Znak zachęty dla kolejnych linii komendy| |''PWD''|Bieżący katalog roboczy| |''EDITOR''|Domyślny edytor tekstowy \\ Np. ''less'' przy otwarciu pliku do edycji skrótem ''v'' uruchamia ''$EDITOR //ścieżka_do_pliku//''| |''LOGNAME'' / ''USER'' \\ ''UID''|Nazwa bieżącego użytkownika \\ Numeryczny identyfikator bieżącego użytkownika| |''LANG''|Język, region i kodowanie znaków; dla Polski zwykle ''pl_PL.UTF-8'' \\ np. ''de_CH.UTF-8'' oznacza niemiecki, Szwajcarię i kodowanie UTF-8| |''TERM''|Nazwa terminala określająca jego techniczne możliwości, takie jak ilość wspieranych kolorów, możliwość przesuwania kursora wstecz etc. Więcej w ''man 7 term'' ([[https://man7.org/linux/man-pages/man7/term.7.html|wersja online]]).| |''COLUMNS'' \\ ''LINES''|Liczba znaków mieszczący się na ekranie w wierszu (=kolumn) \\ Liczba wierszy (linii tekstu) mieszczących się na ekranie| |''RANDOM''|Losowa liczba (generowana na nowo przy każdym odwołaniu się do zmiennej)| Więcej w [[https://www.gnu.org/software/bash/manual/html_node/Shell-Variables.html|dokumentacji Basha]] i [[https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html|standardzie POSIX]] . ==== Ćwiczenia ==== ~~Zadanie.#~~ Ustaw zmienną o nazwie ''//VAR//'' a następnie wyświetl jej wartość. ~~Zadanie.#~~ Ustaw zmienną o nazwie ''//OTHER//'' na wartość zmiennej ''//VAR//''. Wypisz wartość ''//OTHER//'' ~~Zadanie.#~~ Ustaw wartość zmiennej ''//PROG//'' na ''ls'', oraz wartość zmiennej ''//ARG//'' na ''/tmp''. Następnie wpisz w powłokę ''$//PROG// $//ARG//''. ~~Zadanie.#~~ Wyświetl wartości zmiennych z tabeli powyżej. ~~Zadanie.#~~ Zmień wartość zmiennej ''LANG'' na ''ja_JP.UTF-8'' i uruchom program ''date'' oraz ''vim''.\\ Zmień wartość zmiennej ''LANG'' na ''de_DE.UTF-8'' i spróbuj wykonać ''rm -rf /root/.ssh/nope'' oraz ''lscpu''. ~~Zadanie.#~~ Zmień wartość zmiennej ''PS1''. ~~Zadanie.#~~ Spróbuj wykonać komendę ''lspci''. Zmień wartość zmiennej ''PATH'' tak, żeby zawierała ścieżki ''/sbin'' i ''/usr/sbin''. Ponownie spróbuj wykonać komendę ''lspci''. ===== Grupowanie poleceń i podstawienia ===== Działanie powłoki ([[https://pubs.opengroup.org/onlinepubs/009604499/utilities/xcu_chap02.html#tag_02_01|wg. POSIX]], [[https://www.gnu.org/software/bash/manual/html_node/Shell-Operation.html|wg. podręcznika Basha]]): |1. czyta linię| ''echo ${Y} "$Z 2+3=$%%((2+3))%%" > file; ls baz''| |2. rozbija ją na tokeny i rozwiązuje aliasy| ''echo'', ''${Y}'', ''"$Z 2+3=$%%((2+3))%%"'', ''>'', ''file'', '';'', ''ls'', ''--color=auto'', ''baz''| |3. **dzieli na komendy**|''echo'', ''${Y}'', ''"$Z 2+3=$%%((2+3))%%"'' , ''>'', ''file''| |4. **wykonuje podstawienia**| ''echo'', ''a'', ''b'', ''c d 2+3=5'' , ''>'', ''file''| |5. ustawia przekierowania|''echo'', ''a'', ''b'', ''c d 2+3=5''| |6. uruchamia komendę| | ==== Oddzielanie, grupowanie poleceń ==== Polecenia można oddzielać przez: * '';'' – ''cmd ; …'' wykonana ''cmd'' i przejdzie dalej * ''&'' – ''cmd & …'' uruchomi w tle ''cmd'' i przejdzie dalej * operatory logiczne: * ''&&'' – ''a && b'' wykona ''a'' i jeśli ''a'' zakończy się powodzeniem, uruchomi ''b'' \\ ewaluuje do prawdy wtedy i tylko wtedy gdy ''a'' i ''b'' zakończą się powodzeniem * ''||'' – ''a || b'' wykona ''a'' i jeśli ''a'' zakończy się błędem, uruchomi ''b'' \\ ewaluuje do fałszu wtedy i tylko wtedy gdy ''a'' i ''b'' zakończą się błędem Polecenia można grupować przez: * ''{ … }'' – grupuje polecenia w tej powłoce, \\ //Uwaga//: polecenia wewnątrz ''{ … }'' muszą być zakończone przez '';'' lub ''&'' \\ //Uwaga//: między nawiasami wąsatymi i poleceniem muszą być spacje * ''(…)'' – uruchamia polecenia w osobnej powłoce ("podpowłoce"). Przykłady: * ''{ cd /tmp; pwd; }; pwd'' – wejdzie do ''/tmp'' i wypisze dwa razy katalog roboczy ''/tmp'' * ''(cd /tmp; pwd); pwd'' – uruchomi podpowłokę w której wejdzie do ''/tmp'', wypisze katalog roboczy ''/tmp'' w podpowłoce i wypisze bieżący katalog roboczy (np. katalog domowy) w powłoce ++++ Przykłady | {{page>so:variables_substitutions:compound_commands&inline}} ++++ ~~Zadanie.#~~ Napisz polecenie które spróbuje usunąć plik ''//plik//'' i jeśli to się uda, to wyświetli ''udało się'' ~~Zadanie.#~~ Napisz polecenie które spróbuje usunąć plik ''//plik//'' i jeśli to się nie uda, to wyświetli ''nie udało się'' ~~Zadanie.#~~ Policz łączną ilość znaków w wynikach poleceń ''uname -a'' oraz ''date''. ~~Zadanie.#~~ Ustaw zmienną środowiskową wewnątrz ''(…)'' i wyświetl jej wartość po wykonaniu ''(…)''. \\ Następnie powtórz zadanie dla ''{ … }''. ~~Zadanie.#~~ Uruchom w tle grupę poleceń która za pięć sekund wykona program ''fortune''. ==== Podstawienia ==== Podstawienia (//shell expansion// / //word expansion//) polegają na zastąpieniu tekstu (pasującego do konkretnego wzorca) przez określoną wartość ([[https://pubs.opengroup.org/onlinepubs/009604499/utilities/xcu_chap02.html#tag_02_06|wg. POSIX]], [[https://www.gnu.org/software/bash/manual/html_node/Shell-Expansions.html|wg. podręcznika Basha]]). Dostępne podstawienia (w kolejności wykonywania przez BASH): * ''{…}'' – zakres lub lista (Uwaga: POSIX nie definiuje tego podstawienia, jest ono dostępne w Bashu i niektórych innych powłokach) * ''~'' i ''~//username//'' – katalog domowy * ''${…}'' – zmienna / parametr * ''$%%((…))%%'' – arytmetyka (POSIX wymaga tylko wsparcia dla arytmetyki całkowitoliczbowej) * ''$(…)'' i ''`…`'' – podstawia wynik komendy * globy (''*'', ''?'' i ''[…]'') – dopasowuje nazwy plików (było na pierwszych zajęciach) Wewnątrz podwójnych cudzysłowów są wykonywane tylko podstawienia zaczynające się od ''$'' oraz ''`''. \\ Wewnątrz pojedynczych cudzysłowów nie są wykonywane żadne podstawienia. \\ W Bashu funkcjonują też cudzysłowy ''$'…%%'%%'' które pozwalają na wprowadzanie sekwencji poprzedzanych przez odwrócony ukośnik takich jak np. ''\n''; patrz [[https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html|dokumentacja Basha]]. Jeśli wyniki podstawienia (poza dopasowaniem nazw plików) zawierają spację i nie są wewnątrz cudzysłowów, to ich wynik jest rozbijany na osobne tokeny (słowa). \\ Np: ''ls $(echo dwa słowa)'' spróbuje wypisać katalog ''dwa'' i katalog ''słowa'', a ''ls "$(echo dwa słowa)"'' spróbuje wypisać katalog ''dwa słowa'' === Zakres / lista – nawiasy wąsate === |liczby (lub litery) z podanego zakresu \\ format: ''{początek..koniec..krok}''|**''{1..5}''**|''1 2 3 4 5''| |:::|''file{15..20}.txt''|''file15.txt file16.txt file17.txt file18.txt''| |:::|''{0..9..3}''|''0 3 6 9''| |:::|''{a..z..2}''|''a f k p u z''| |wymienione po przecinkach elementy \\ format: ''{element1,element2,…}''|**''{a,b,d,f}''**|''a b d f''| |:::|**''/tmp/{ala,ma,kota}.txt''**|''/tmp/ala.txt /tmp/ma.txt /tmp/kota.txt''| |:::|''file{,.gz,.bzip2,.xz}''|''file file.gz file.bzip2 file.xz''| ~~Zadanie.#~~ Stwórz puste pliki ''plik_1'', ''plik_2'', ..., ''plik_9'' (używając nawiasów wąsatych do stworzenia listy plików). ~~Zadanie.#~~ Przeanalizuj co robi polecenie: ''echo -e " " {A..H} '\n'{8..1}'' ~~Zadanie.#~~ Wykonaj komendę ''stat'' dla plików ''/var/log/btmp'', ''/var/log/mtmp'', ''/var/log/wtmp'' (używając nawiasów wąsatych do stworzenia listy plików). === Katalogi domowe === |katalog domowy bieżącego użytkownika |**''~''**|''///home/username//'' | |katalog domowy użytkownika username|''~username''|''/home/username''| ~~Zadanie.#~~ Wypisz ścieżkę do swojego katalogu domowego korzystając z ''~''. ~~Zadanie.#~~ Wejdź do katalogu domowego użytkownika ''wwwrun''. === Zmienne / parametry === Poza podstawianiem wartości zmiennej przez **''$ZMIENNA''** można też użyć składni **''${ZMIENNA}''**. Pozwala to np., na napisanie ''${SIZE}MB''. Wyrażenie ''${#ZMIENNA}'' podstawia długość (ilość znaków) zmiennej. \\ Np. ''X='żółć'; echo ${#X}'' wypisze ''4''. Dodając za nazwą zmiennej, ale wewnątrz nawiasów modyfikatory, można uzyskać: * ''${X:-wyrażenie}'' – podstawia wartość ''X'' lub, jeśli ''X'' nie istnieje lub jest puste, podstawia wartość ''wyrażenie'' * ''${X:+wyrażenie}'' – podstawia wartość ''wyrażenie'' jeśli ''X'' ma niepustą wartość, inaczej podstawia pustą wartość * ''${X:liczba}'' – podstawia wartość ''X'' pomijając ''liczba'' znaków * ''${X::liczba}'' – podstawia pierwsze ''liczba'' znaków z wartości ''X'' * ''${X:ile_pominąć:ile_wypisać}'' – podstawia pierwsze ''ile_wypisać'' znaków z wartości ''X'' zaczynając od znaku ''ile_pominąć+1'' * ''${X%wzorzec}'' – ucina z końca najkrótsze dopasowanie do ''wzorzec'' (o ile takie jest) \\ ''${X%%wzorzec}'' – ucina z końca najdłuższe dopasowanie do ''wzorzec'' (o ile takie jest) * ''${X#wzorzec}'' – ucina z początku najkrótsze dopasowanie do ''wzorzec'' (o ile takie jest) \\ ''${X##wzorzec}'' – ucina z początku najdłuższe dopasowanie do ''wzorzec'' (o ile takie jest) * ''${X/wzorzec/wyrażenie}'' – zamienia pierwsze wystąpienie ''wzorzec'' przez ''wyrażenie'' \\ ''${X%%//%%wzorzec/wyrażenie}'' – zamienia wszystkie wystąpienia ''wzorzec'' przez ''wyrażenie'' \\ Uwaga: ''${X/…}'' i ''${X%%//%%…}'' są rozszerzeniem Basha ++++ Przykłady | {{page>so:variables_substitutions:variable_expansion&inline}} ++++ ~~Zadanie.#~~ Wpisz do zmiennej ''FILE'' nazwę pliku. Następnie wykonaj polecenie które zmieni nazwę tego pliku dodając na koniec rozszerzenie ''.txt''. ~~Zadanie.#~~ Czy rozwiązanie poprzedniego zadania zadziała dla nazwy pliku ze spacją? Sprawdź. Jeśli nie - popraw je. ~~Zadanie.#~~ Załóż że w zmiennej ''M'' jest numer miesiąca (np. ''//05//''), a w zmiennej ''D'' jest numer dnia (np. ''//08//''). Wykorzystując te zmienne przypisz wartość zmiennej ''LOGFILE'' tak, żeby miała postać ''myprog_//miesiąc//_//dzień//.log''. \\ Następnie przekieruj wynik polecenia ''echo hello world'' do wartości tej zmiennej. ~~Zadanie.#~~ Napisz komendę, która przeniesie pliki ''//file1//'' i ''//file2//'' do katalogu wskazanego w zmiennej ''TARGET'' lub, jeśli ta zmienna jest pusta, do katalogu ''/tmp''. ~~Zadanie.#~~ Przyjmij, że w zmiennej ''S'' jest pełna ścieżka do pliku (np. ''/home/user/some dir/some file.sh''). \\ Do zmiennej ''D'' przypisz katalog w którym znajduje się plik wskazany przez ''S'', a do ''F'' przypisz samą nazwę pliku wskazanego przez ''S''. \\ Następnie wypisz wartość ''D'' oraz ''S''. ~~Zadanie.#~~ Przygotuj komendę która zmienia nazwę pliku wskazanego przez zmienną ''F'' tak, by nazwa nie zawierała spacji. === Podstawianie wyników poleceń === Wyrażenia ''**$(**//cmd//**)**'' oraz ''`//cmd//`'' zostaną zastąpione wynikiem polecenia ''//cmd//''. \\ Trzeba pamiętać że w linii ''//cmd// … $(//sub//) …'' najpierw zostanie w całości wykonane polecenie ''//sub//'', a potem jego wynik będzie podstawiony w miejsce ''$(//sub//)''. Zaleca się używać składni ''$(//cmd//)'', składnia ''`//cmd//`'' jest wspierana dla kompatybilności wstecznej. \\ Wyrażenia ''$(…)'' można dowolnie zagnieżdżać w sobie. ~~Zadanie.#~~ Napisz komendę która wyświetli napis ''W bieżącym katalogu znajduje się //N// plików'', gdzie w miejsce ''//N//'' zostanie wstawiona faktyczna liczba plików. ~~Zadanie.#~~ Komenda ''readlink -f //nazwa_pliku//'' (i ''realpath //nazwa_pliku//'') rozwiązuje wszystkie symlinki i pokazuje kanoniczną ścieżkę do pliku. Przypisz do zmiennej ''DIR'' kanoniczną ścieżkę do ''..'' (do katalogu nadrzędnego). ~~Zadanie.#~~ Komenda ''date +%H_%M_%S'' wypisuje bieżący czas. Napisz komendę która przekierowuje wynik programu ''pstree -au'' do pliku ''procesy_//CZAS//.log'', gdzie w miejscu ''//CZAS//'' ma pojawić się bieżący czas. ~~Zadanie.#~~ Komenda ''iconv -t ascii%%//%%TRANSLIT'' zamienia znaki ze standardowego wejścia (lub wskazanych plików) na kodowanie ASCII, zamieniając niekodowalne znaki na najbliższe odpowiedniki. Przygotuj komendę która zmienia nazwę pliku wskazanego przez zmienną ''F'' tak, by nowa nazwa składała się tylko ze znaków ASCII. ===== Arytmetyka ====== Powłoka wspiera obliczenia (przynajmniej) całkowitoliczbowe. \\ Standard POSIX definiuje tylko składnię ''**$%%((%%**…**))**''. \\ Bash dodatkowo posiada polecenie ''let …'' wykonujące podane działania. Wewnątrz ''%%$((…))%%'' / ''let …'' można: * pomijać ''$'' przed nazwami zmiennych, np. ''%%x=$((x+2))%%'' * używać operatora przypisania, np. ''%%echo $((y=2**5, z=10^6))%%'' przypisze do ''y'' wartość ''25 = 32'', przypisze do z wartość ''10 xor 6 = 12'', wyliczy ''32, 12 = 12'' (operator przecinka z C) i wykona ''echo 12'' * zawartość ''%%$((…))%%'' jest traktowana jakby była w podwójnych cudzysłowach,\\ zawartość ''let …'' liczy każdy z argumentów z osobna, \\ np: ''X=$%%((%% ( 2 + 2 ) * 2 ))'', ''let "X = ( 2 + 2 ) * 3"'', ''let X=(2+2)*3'' i ''let X=(2+2)*3 Y=2*X+1'' są poprawne, ale ''let X = ( 2 + 2 ) * 3'' już nie * wewnątrz ''let …'' trzeba escape'ować znaki specjalne, np. ''let X=0xff\&1066'' / ''let "X = 0xff & 1066"'' POSIX [[https://pubs.opengroup.org/onlinepubs/9699919799/utilities/expr.html|standaryzuje]] też program ''expr'' który wykonuje podane obliczenie i wypisuje jego wynik na standardowe wejście. Wyżej wymieniona funkcjonalność powłoki / program należy traktować jako narzędzia pomocnicze do pisania skryptów, a nie narzędzia do obliczeń. \\ Proste, sensowne kalkulatory dla powłoki to np. ''[[https://pl.wikipedia.org/wiki/Bc_(Unix)|bc]]'', ''[[https://github.com/lcn2/calc|calc]]'' i ''[[https://github.com/Qalculate/libqalculate|qalc]]''. ~~Zadanie.#~~ Ustaw zmienne ''X'' i ''Y'' na wybrane dwucyfrowe liczby, a następnie: \\       • zwiększ ''X'' o 1 \\       • zwiększ ''Y'' o 2 \\       • ustaw zmienną ''Z'' na iloczyn ''X'' i ''Y'' \\       • wylicz resztę z dzielenia ''Z'' przez 128. ~~Zadanie.#~~ Przygotuj polecenie, które: \\       • przypisze bieżący czas w nanosekundach (wynik komendy ''date +%s%N'') do zmiennej ''START'' \\       • wykona komendę ''sleep 1s'' \\       • przypisze bieżący czas w nanosekundach (wynik komendy ''date +%s%N'') do zmiennej ''END'' \\       • wypisze różnicę między ''END'' a ''START'' ===== Aliasy ====== Alias ([[https://pubs.opengroup.org/onlinepubs/9699919799/utilities/alias.html|POSIX]], [[https://www.gnu.org/software/bash/manual/html_node/Aliases.html|Bash]]) to słowo, które jeśli wystąpi w miejscu komendy, to zostanie zastąpione wcześniej ustalonym tekstem. \\ Przykładowo: wiele dystrybucji Linuksa automatycznie ustawia alias ''la'' na wartość ''ls -la'' i aliasuje ''ls'' na ''ls --color=auto''. \\ Przy takich ustawieniach po wpisaniu ''la'' najpierw następuje zmiana ''la'' na ''ls -la'' (i wyłączenie dla przetwarzania tego polecenia aliasu ''la''), potem ''ls'' na ''ls --color=auto'' (i wyłączenie dla przetwarzania tego polecenia aliasu ''ls''). Aby utworzyć nowy alias, należy wykonać komendę: \\ ''**alias** //słowo//=//na_co_ma_się_tłumaczyć//'' \\ Np: ''alias la="ls -la"''\\ Alias można usunąć komendą ''unalias //słowo//''. Wpisanie komendy ''alias'' bez argumentów wypisze wszystkie ustawione aliasy. \\ Żeby sprawdzić czy dany wyraz ''//co//'' jest programem, aliasem czy jeszcze czymś innym, można użyć komendy ''type //co//''. Aliasy, podobnie jak zmienne, są ustawiane dla bieżącej powłoki. ~~Zadanie.#~~ Stwórz alias ''year'' który będzie równoważny ''cal -my''. \\ Użyj tego aliasu. Użyj tego aliasu żeby wyświetlić kalendarz na rok 2025. ~~META: language = pl ~~