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.
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?
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:
PS1
– zmienna lokalna określająca wygląd znaku zachęty,PS2
– zmienna lokalna określająca wygląd uzupełniającego znaku zachęty,PATH
– zmienna globalna określająca ścieżki przeszukiwań,HOME
– zmienna globalna przechowująca ścieżkę do katalogu domowego.
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
?
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?
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.
- pojedyncze cudzysłowy
' '
– znajdujący się wewnątrz ciąg znaków nie jest w żaden sposób przetwarzany, a znajdujące się wewnątrz znaki specjalne traktowane są jako zwykłe symbole; - podwójne cudzysłowy
" "
– znajdujący się wewnątrz ciąg znaków jest przetwarzany, dlatego niektóre symbole specjalne (np. znak dolara), które mają być traktowane jako symbole, powinny być poprzedzane znakiem odwróconego ukośnika; - apostrofy
` `
– reprezentuje ciąg znaków będących efektem (w sensie standardowego wyjścia) wykonania polecenia znajdującego się wewnątrz; warto tu zwrócić uwagę na zbieżność z poznaną już konstrukcją$()
.
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:
- znak średnika oddziela polecenia, które będą wykonywane jedno po drugim, niezależnie od efektu tego wykonywania, np.:
$ cd ~; touch plik.txt; ls -l plik.txt
&&
oddziela polecenia, których wykonanie jest uzależnione od prawidłowego zakończenia wszystkich wcześniejszych, np.:$ cd /dev && touch plik.txt && ls -l plik.txt
||
oddziela polecenia, których wykonanie jest uzależnione od nieprawidłowego zakończenia któregokolwiek z wcześniejszych, np.:$ (cd /dev && touch plik.txt) || echo "Nie udało się"
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 .
?
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.
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.
$ 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
.
$ 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.