Moduł 4

Materiały do zajęć z Systemów operacyjnych prowadzonych na Wydziale Matematyki i Informatyki Uniwersytetu im. Adama Mickiewicza w Poznaniu.

« Wróć do spisu materiałów

Do tej pory traktowaliśmy powłokę Bash jako interfejs wiersza poleceń systemu. Pełni ona jednak dodatkowo funkcję interpretera języka skryptowego o tej samej nazwie (Bash). Celem tych i kolejnych ćwiczeń jest zapoznanie się z podstawami tworzenia skryptów powłoki Bash.

Zmienne powłoki, polecenia echo i eval

Zmienne lokalne

Aby utworzyć zmienną lub zmienić jej wartość, należy wykonać polecenie

$ zmienna=wartość

Istotny jest tu brak odstępów przed i po znaku równości.

Wykonaj następuące polecenia:

$ a=1
$ a= 1
$ a =1
$ b=Ala
$ b=Ala ma kota
$ b="Ala ma kota"
$ b='Ala ma kota'

Które z nich zakończyły się wystąpieniem błędu? Spróbuj utworzyć kilka zmiennych lokalnych w analogiczny sposób. Sprawdź empirycznie, jakie warunki musi spełniać prawidłowa nazwa zmiennej.

Zobacz odpowiedź

Polecenia a= 1, a =1 oraz b=Ala ma kota zakończyły się wystąpieniem błędu.

Aby odwołać się do zapisanej zmiennej, należy poprzedzić jej nazwę symbolem $.

Wykonaj następujące polecenia:

$ a="ls"
$ b=$a
$ $b

Co się stało? Na podstawie tego eksperymentu spróbuj odpowiedzieć na pytanie, jak powłoka Bash zachowuje się, gdy w poleceniu następuje odwołanie do zmiennej.

Wykonaj poniższe polecenia:

$ a=5
$ sleep $a
$ a="/usr/bin"
$ b=less
$ ls -l $a | $b

Czy powłoka rozróżnia zmienne liczbowe od znakowych?

Sprawdź, jak interpretowane są zmienne, które nie istnieją.

Do zmiennej możemy też przypisać efekt (w rozumieniu standardowego wyjścia) wykonania okreslonego polecenia. Służy do tego specjalna struktura $().

Wykonaj w swoim katalogu domowym następujące polecenia:

$ touch plik.txt
$ plik="plik.txt"
$ wynik=$(ls -l $plik)
$ echo $wynik
$ wynik2="Wynik to $(ls -l $plik)"
$ echo $wynik2
$ wynik3='Wynik to $(ls -l $plik)'
$ echo $wynik3

Zapoznaj się z efektami. Jak myślisz, jak działa polecenie echo?

Podobnie zresztą można wykonywać podstawowe operacje arytmetyczne, korzystając ze struktury $(()).

Wykonaj w swoim katalogu domowym następujące polecenia:

$ a=10 b=15
$ a=$((a*2))
$ b=$(((b-5)+a))
$ sleep $b

Jaki parametr zostanie przekazany do programu sleep? Czy efekt będzie taki sam, jeśli nazwy zmiennych wewnątrz wyrażeń poprzedzimy znakiem dolara?

Zwróć uwagę na fakt, że możemy modyfikować wartość więcej niż jednej zmiennej w jednym poleceniu.

Kiedy przekazujemy do powłoki Bash polecenie, jej interpreter przegląda je w poszukiwaniu elementów, które należałoby w jakiś sposób przetworzyć. Dzieje się to zanim polecenie zostanie wykonane. Wyjątkiem są operacje przypisania wartości do zmiennych – jeśli polecenie jest ciągiem przypisań wartości do zmiennych, przypisania te będą realizowane po kolei.

Wykonaj w swoim katalogu domowym następujące polecenia:

$ a=10 b=15
$ echo $((a*2))
$ echo $(($a*2))

Jaka jest różnica w efektach wywołania polecenia echo? Jaka jest różnica w tym, jak na polecenie reaguje powłoka?

Zobacz odpowiedź

W ostatnim przypadku powłoka wykonuje tak naprawdę proces interpretacji dwukrotnie – raz przekształcając wyjściowe polecenie w echo $((10*2)), w drugim kroku zaś w echo 20.

Zmienne środowiskowe

Powłoka Bash pozwala na definiowanie stałych i zmiennych lokalnych i globalnych (zwanych środowiskowymi). Zmienne te wpływają na funkcjonowanie powłoki i wykonywanych w niej poleceń.

Kiedy powłoka Bash jest uruchamiana, wykonywane są polecenia zebrane w określonych plikach, np. /etc/profile czy ~/.bashrc. Duża część tych poleceń wpływa na wartości zmiennych lokalnych i środowiskowych. Te ostatnie dostępne są nie tylko wewnątrz powłoki, ale także dla uruchamianych w niej programów.

Aby uczynić ze zmiennej lokalnej zmienną środowiskową (globalną), można skorzystać z polecenia export. Przyjmuje ono jako parametry nazwy istniejących zmiennych lub ich definicje. Tym samym polecenie

$ export a=1 b=2 c=3

jest równoważne ciągowi poleceń

$ a=1
$ b=2 c=3
$ export a b
$ export c

Wykonaj następujące polecenia:

$ a=1 b=2 c=3
$ export a b
$ bash
$ echo $a $b $c
$ exit

Skomentuj wyniki. Co dokładnie się stało?

Czasami istnieje konieczność wykonania polecenia ze zmodyfikowanym środowiskiem. Taką możliwość daje program env.

Zapoznaj się z efektem wykonania następujących poleceń:

$ a=1 b=2 c=3
$ export a b
$ bash
$ echo $a $b $c
$ exit
$ env b=5 bash
$ echo $a $b $c
$ exit
$ echo $a $b $c

Najważniejsze zmienne

Funkcjonowanie powłoki Bash w dużej mierze zależy od tego, jakie wartości przyjmują określone zmienne. Najważniejsze z nich to:

Programy w systemach uniksopodobnych są, podobnie jak wiele innych elementów, plikami. Pliki te mogą znajdować się w różnych miejscach struktury plików. Mimo to, gdy wpisujemy w powłoce polecenie

$ ls

system wie, że ma wykonać program /bin/ls. Skąd? Informacja o tym, które katalogi mają być przeszukiwane w poszukiwaniu wykonywalnych elementów o wskazanej nazwie, przechowywana jest w zmiennej globalnej PATH.

Wykonując polecenie printenv, zapoznaj się z listą zmiennych środowiskowych. Sprawdź, jaką wartość ma zmienna PATH.

Co by się stało, gdyby program ls znajdował się w więcej niż jednym katalogu wskazanym w zmiennej PATH?

Zobacz odpowiedź

W takim wypadku wykonałby się program ls z tego katalogu, który jest najwcześniej na liście.

Wykonaj następujące polecenia:

$ bash
$ PS1="Wpisz coś: "
$ PS2="Wpisuj dalej> "
$ echo "Ala ma ko
$ ta"
$ exit

Jak zachowuje się powłoka? Jak na jej zachowanie wpływa otwarcie cudzysłowu? Jaki inny sposób łamania długich wierszy można wykorzystywać w powłoce Bash?

Zobacz odpowiedź

Powłoka Bash pozwala na łamanie wierszy przy użyciu symbolu \.

Polecenie echo

W eksperymentowaniu z powłoką przydatne okaże się polecenie echo. Działa ono w bardzo prosty sposób – wypisuje w postaci ciągu znaków na wyjściu jego argumenty, oddzielając je znakiem odstępu.

Wykonaj następujące polecenia:

$ echo
$ echo 1
$ echo Ala ma kota
$ echo  Ala  ma  kota
$ echo "Ala ma kota"
$ echo " Ala  ma  kota"

Jakie wnioski można wyciągnąć z tego eksperymentu?

Polecenie echo pozwala nam przekonać się także, jak powłoka interpretuje ciągi znaków w różnego rodzaju cudzysłowach.

Jeśli ciąg znaków nie jest umieszczony w cudzysłowach, jest zwykle traktowany tak samo jak ciąg umieszczony w podwójnych cudzysłowach, przy czym wówczas każdy znak specjalny należy poprzedzić znakiem odwróconego ukośnika, a symbol odstępu oznacza koniec tego ciągu.

Wykonaj następujące polecenia:

$ program=ls
$ echo $program
$ echo 'Program $program' "Program $program"
$ echo "Program $program"
$ echo "${program}program"
$ echo `$program`

Domyślnie, polecenie echo kończy swoje działanie wypisaniem znaku nowej linii. Aby temu zapobiec, należy wywołać je z opcją -n. Przydatność tego rozwiązania stanie się jasna, gdy będziemy łączyć polecenia read oraz echo.

Polecenie eval

Wbudowane polecenie powłoki eval łączy przekazane mu jako argumenty ciągi znaków i wykonuje je jako jedno polecenie.

Wykonaj poniższe polecenia:

$ a=1 a1=2
$ eval echo $a
$ eval echo "\$a"
$ eval echo "\$a$a"

Określ, w jaki sposób powłoka przetwarza poszczególne polecenia. Jeśli nie czujesz się zbyt pewnie z poleceniem eval, poeksperymentuj więcej.

Powłoka Bash – tips & tricks – część II

Wykonywanie warunkowe

Każdy program uruchomiony w powłoce zwraca do systemu, po swoim zakończeniu, pewną całkowitą wartość liczbową z przedziału od 0 do 255, tzw. status wyjścia (exit status). Przyjmuje się, że jeśli program zwrócił wartość 0, to zakończył się prawidłowo. Inne liczby mogą być związane z konkretnymi błędami, które wystąpiły w czasie jego wykonywania.

Status wyjścia ostatnio wykonanego polecenia przechowywany jest w zmiennej $?. System zaś udostępnia dwa proste programy: true, który zawsze kończy się powodzeniem i false, który zawsze kończy się błędem.

Sprawdź, jakie statusy wyjścia zwracają programy true i false. Korzystając z polecenia whereis dowiedz się, gdzie na dysku znajdują się te programy.

Wykonując polecenia, możemy uzależnić wykonanie niektórych z nich od tego, jakie statusy wyjścia zwracają inne. Służą do tego trzy metasymbole powłoki Bash: znak średnika, && oraz ||. Tak oto:

Jak widać, nawiasy okrągłe mogą posłużyć do grupowania poleceń, jednak w swojej naturze powodują wykonanie polecenia w specjalnie do tego celu uruchomionym podprocesie powłoki.

Spróbuj wykonać następujące polecenia:

$ cd /dev; touch plik.txt; ls -l ~
$ cd /dev && touch plik.txt
$ cd /dev && touch plik.txt && ls -l plik.txt
$ (cd /dev && touch plik.txt) || echo "Nie udało się"

Sprawdź, jakie statusy wyjścia zwracają.

Polecenia w plikach

Naturalne pytanie brzmi: czy można zapisać zbiór poleceń w pliku i wykonać je wszystkie na raz? Odpowiedź jest pozytywna. Jeśli plik tekstowy będzie zawierać maksymalnie jedno polecenie w każdym wierszu, to można zmusić powłokę, aby wykonała je tak, jakbyśmy wprowadzali je jedno po drugim w powłoce.

Przykładowo, jeśli plik ~/kod zawiera polecenia, to można je wykonać, wywołując polecenie

$ . ~/kod

Zwróć szczególną uwagę na różnicę między symbolem kropki (.) jako poleceniem wewnętrznym powłoki a tym samym symbolem jako nazwą dowiązania stałego do bieżącego katalogu.

Utwórz w swoim katalogu domowym plik o nazwie kod i umieść w nim następujące polecenia:

cd /dev
ls
cd -

Wykonaj te polecenia, korzystając z metasymbolu kropki. Porównaj zachowanie powłoki w stosunku do sytuacji, w której polecenia te byłyby wprowadzane ręcznie, jedno po drugim. Jakie uprawnienia należy posiadać do pliku kod, aby można było wykonać zawarte w nim polecenia z użyciem metasymbolu .?

Zobacz odpowiedź

Nie ma różnicy w efektach wykonania, poza tym, że wraz z wykonywanymi poleceniami nie pojawia się nowy znak zachęty. To znaczy, ciąg poleceń traktowany jest jako jedno rozbudowane polecenie. Do wykonania poleceń z takiego pliku wystarczy uprawnienie do odczytu.

Dowiedz się, jaka jest techniczna różnica pomiędzy wykonaniem poleceń z pliku a wykonaniem skryptu powłoki. Swoje przypuszczenia zweryfikujesz na kolejnych ćwiczeniach.

Zbiory

Metasymbole { oraz } pozwalają generować na poziomie powłoki zbiory (ciągi) argumentów. Najłatwiej będzie to zrozumieć na przykładach.

Wykonaj następujące polecenia:

$ echo a{b}d
$ echo a{b,c}d
$ echo {a..c}
$ echo {a,b,c}{d,e,f}
$ echo {a,b,c} {d,e,f}
$ echo {5..100}
$ echo {A..Z}{000..100}
$ echo {00..100}
$ echo {0..100}
$ echo {AA..ZZ}
$ echo To jest wyr{az,ocznia,ko}
$ echo "To jest wyr{az,ocznia,ko}"
$ echo "To jest wyr"{az,ocznia,ko}

Zastanów się nad efektami.

Zobacz odpowiedź

W echo a{b}d, echo {AA..ZZ} oraz echo "To jest wyr{az,ocznia,ko}" symbol zbioru zostanie potraktowany jako ciąg znaków.

Sprawdź, jak zachowa się powłoka, gdy wykonasz w niej następujące polecenia:

$ a=1 b=50
$ echo {$a..$b}

Dlaczego tak się dzieje? Zmodyfikuj drugie polecenie tak, aby zadziałało w oczekiwany sposób.

Zobacz odpowiedź

$ eval echo {$a..$b}

Utwórz w swoim katalogu domowym katalog Daty. W jego wnętrzu utwórz 365 katalogów o nazwach odpowiadających kolejnym datom w roku 2015, zapisanym w postaci RRRR-MM-DD.

Zobacz odpowiedź

$ mkdir 2015-{01,03,05,07,08,10,12}-{01..31} \
        2015-{04,06,09,11}-{01..30} 2015-02-{01..28}

Z katalogu, o którym mowa w poprzednim ćwiczeniu, usuń te katalogi, które są związane z miesiącem lutym, majem oraz wrześniem.