sop 2009 lab08


Uniwersytet w Białymstoku
Wydział Matematyki i Informatyki
Instytut Informatyki
Materiały bazowe do zajęć z przedmiotu:
Systemy operacyjne
Laboratorium nr 08
Temat:
Zmienne tablicowe, pętle for, while i until
mgr Adam Bonda
Białystok 2008
Cel laboratorium: Zapoznanie się z u\yciem pętli for, while, until w połączeniu z
rozszerzoną arytmetyką parametrów ich wywołania i zastosowaniem zmiennych
tablicowych.
Podwójne nawiasy (( wyrażenie )) oraz podwójne nawiasy kwadratowe
[[ wyrażenie ]]
Podwójne nawiasy pozwalają na bezpośrednie wprowadzanie bardziej zaawansowanych
formuł matematycznych na poziomie porównań komend strukturalnych (if-then) w
odró\nieniu od polecenie test, które oferowało u\ycie tylko prostych operacji
arytmetycznych.
Składnia wyrażenie mo\e być zarówno matematycznym jak i porównawczym
wyra\eniem.
$ cat skrypt.sh
#!/bin/bash
var_1=5
if (( $var_1 ** 2 > 20 ))
then
(( var_2 = $var_1 ** 2 ))
echo "Kwadratem liczby $var_1 jest $var_2"
fi
$ ./skrypt.sh
Kwadratem liczby 5 jest 25
Dozwolone jest stosowanie następujących operatorów w większości znanych ju\ z
poprzednich zajęć: i++, i--, ++i, --i, !, ~, **, <<, >>, &, |, &&, ||.
Podwójne nawiasy kwadratowe pozwalają na u\ycie zaawansowanych porównań ciągów
znaków. Porównywanie stringów zapisanych, jako wyra\enia otoczone tymi symbolami
odbywa się podobnie jak w przypadku polecenia test. Symbol podwójnego nawiasu
kwadratowego zapewnia jednak skorzystanie z dodatkowej opcji, której polecenie test nie
posiada, porównywania wzorców. W porównywanym wzorcu mo\liwe jest zdefiniowanie
wyra\enia regularnego, które porównywane jest z ciągiem znakowym.
$ cat skrypt.sh
#!/bin/bash
if [[ $SHELL == xt* ]]
then
echo "Używasz terminala $SHELL"
else
echo "Hmm, a mówiłem, żeby nie instalować nowych \"wynalazków\""
echo "ale z drugiej strony, może ten terminal zjada mniej zasobów?"
fi
Zmienne tablicowe
Powłoka bash umo\liwia korzystanie z jednowymiarowych tablic. Nazywane są one
zmiennymi tablicowymi, poniewa\ mogą przechowywać więcej ni\ jedną wartość. Ka\da
wartość indeksowana jest liczbą całkowitą, a indeks elementu tablicy umieszcza się w
nawiasach kwadratowych, jak podano w przykładzie ni\ej:
TABLICA1[0]="wartość zerowego"
TABLICA1[1]=drugi
TABLICA1[2]=3
TABLICA1[4]=iv
Mo\liwa jest inicjacja wielu elementów tablicy naraz:
TABLICA2=( "wartość zerowego" pierwszego [9]=numer iv )
Elementy tablicy rozdziela się spacjami i wylicza poczynając od elementu o indeksie 0. Jak
widać powy\szym przykładzie mo\na tworzyć elementy o konkretnym indeksie, poprzez
zadeklarowanie jego numeru w nawiasach klamrowych.
Odwołanie się do wartości elementu zmiennej tablicowej mo\liwe jest poprzez
zastosowanie wyra\enia:
${nazwa_zmiennej_tablicowej[indeks]}
$ echo ${TABLICA2[0]}
wartość zerowego
Wyświetlony został element tablicy o indeksie zero
$ echo ${TABLICA2[3]}
Element o indeksie 3 nie istnieje, zwrócona więc została wartość pusta.
$ echo ${TABLICA2[9]}
numer
Element o indeksie 9, przechowuje wartość  numer .
$ echo ${TABLICA2[10]}
iv
Jeśli w tablicy zadeklarowano wartość nie będącą kontynuacją poprzednich indeksów to
tworzone dalej elementy (bez podania numeru) indeksowane są kolejnymi liczbami
całkowitymi. W tym przypadku nie podanie indeksu wartości  iv spowodowało
automatyczne przydzielenie jej wartości (10) wy\szej o jeden ni\ indeks ją poprzedzający
(9).
U\ycie zmiennych specjalnych, jako indeksów jest równie\ dozwolone np.:
${zmienna[*]}, "${zmienna[*]}", ${zmienna[@]}.
$ echo ${TABLICA2[*]}
wartość zerowego pierwszego numer iv
Wyświetlone zostały wszystkie elementy tablicy
$ echo "${TABLICA2[*]}"
wartość zerowego|pierwszego|numer|iv
Wyświetlone zostały wszystkie elementy tablicy, a podwójne cytowania spowodowało
u\ycie separatora zdefiniowanego zmienną systemową IFS (ang. internal field separator).
Domyślnie powłoka bash, jako separatorów u\ywa: spacji, tabulatora, znaku nowej linii.
Podobnie jak w przypadku zmiennych, zmienne tablicowe i ich elementy usuwa się
poleceniem unset.
unset TABLICA2[9]
$ echo ${TABLICA2[*]}
wartość zerowego pierwszego iv
Usunięto element o indeksie 9.
$ echo ${TABLICA2[9]}
$ echo ${TABLICA2[10]}
iv
Wartość elementu indeksowanego liczbą 9 jest pusta. Nie nastąpiła jednak reindeksacja
elementów i następny, w stosunku do usuniętego elementu, posiada w dalszym ciągu
indeks o numerze 10.
unset TABLICA2
$ echo ${TABLICA2[*]}
Usunięta została zmienna tablicowa i wszystkie jej elementy.
Instrukcja wyboru case
Działanie komendy case jest podobne do konstrukcji warunkowej if-then. Jak sama
nazwa wskazuje dotyczy ona wyborów, a więc określenia, który fragment kodu zostanie
wykonany na zasadzie sprawdzania przynale\ności testowanej wartości do z góry
określonego zbioru.
Ogólna postać tej konstrukcji przedstawia się następująco:
case zmienna in
szablon1 | szablon2) komenda1;;
szablon3)
komenda2
komenda3
komenda3
;;
*) domyślne_komendy;;
esac
Po komendzie case, podawana jest wartość, która ma być oceniana. Mo\e to być
zmienna, lub zdefiniowane słowo. Z oczywistych względów wstawia się tam zazwyczaj
nazwę zmiennej  zwiększa automatyczność konstrukcji. Je\eli w słowie występują
spacje, to całość nale\y otoczyć znakami podwójnego cytowania. Kolejnymi elementami
case są: słowo kluczowe in i lista opcji do wyboru.
Ka\dą z opcji umieszcza się w nowej linii i kończy zamykającym nawiasem. Je\eli shell
ustali zgodność zmiennej z którymś z szablonów to wykonana zostanie komenda, bądz
lista wprowadzonych komend umieszczonych za danym ciągiem szablonów, po czym
obsługa case jest kończona.
W szablonach dozwolone jest stosowanie wyra\eń regularnych zawierających metaznaki:
* (dowolny napis) ? (dowolny znak) [...] (określony zbiór znaków).
Konstrukcja case nie sprawdza, czy wszystkich szablonów pasujących do testowanego
słowa/zmiennej. Jeśli shell natrafi na pierwszy zgodny ze zmienną szablon to lista komend
po nim występujących jest wykonywana.
case "$1" in
*) echo "zgodność 1";;
*) echo "zgodność 2";;
esac
W powy\szym przykładzie zostanie więc wyświetlony tylko napis  zgodność 1 .
case "$1" in
start|uruchom)
echo "$0 został wywołany z pierwszym argumentem:"
echo "\"start\" lub \"uruchom\""
stop|zatrzymaj)
echo "$0 został wywołany z pierwszym argumentem:"
echo "\"stop\" lub \"zatrzymaj\""
*)
echo "$0 został wywołany z pierwszym argumentem"
echo "nierozpoznawalnym przez składnię skryptu."
esac
Mo\liwe jest poddawanie kilku szablonów alternatywnych (poprzez u\ycie symbolu
podziału potoku |), przy których zostanie wykonany ten sam ciąg instrukcji.
Pętla for
Powłoka bash oferuje dwa rodzaje pętli. Pierwszy z nich stanowi pętla for. Pobiera ona,
jako parametr, listę napisów i dla ka\dego, jeden raz wykonuje zadeklarowaną składnię.
Drugi rodzaj pętli stanowią polecenia while i until, które w odró\nieniu od for pobierają
wyra\enie warunkowe, jako parametr i wykonują się tak długo, jak wyra\enie jest
prawdziwe (w przypadku while), lub gdy jest fałszywe (w przypadku until).
Bash poprzez strukturę for umo\liwia wykonywanie pętli iteracyjnej na zbiorze wartości.
Ka\da iteracja (powtórzenie) wykonuje tylko jeden raz określony zestaw instrukcji dla
ka\dej wartości zbioru przekazanej w postaci parametru.
Ogólna konstrukcja pętli for, którą obsługuje powłoka bash ma następującą postać:
for var in lista_słów
do
zestaw_instrukcji
done
lista_słów przekazywana jest pętli w formie parametru po słowie kluczowym in,
poprzedzonym nazwą zmiennej var. Podczas ka\dej iteracji, zmienna var przechowuje
aktualną wartość z listy. Pierwsza iteracja wykorzystuje pierwszy element listy, kolejna
drugi, trzeci i tak, i tak a\ do ostatniego.
Ciąg instrukcji zawierający zmienną var, jaki ma zostać wykonany dla ka\dego elementu
listy umieszcza się pomiędzy znacznikami do i done.
for i in $*
do
echo wartość argumentu: $i
done
Wywołanie powy\szego skryptu z kilkoma parametrami zwróci wartości tych parametrów
poprzedzone ciągiem tekstowym.
for i in `ls *.sh`
do
echo $i
done
Jak widać na powy\szych przykładach, lista_słów mo\e być reprezentowaną zmienną
przechowującą zbiór wartości, lub mo\e być po prostu listą elementów wprowadzonych
przez u\ytkownika, bądz te\ wyprowadzonych za pomocą zewnętrznej komendy. Ka\dy
element mo\e być cyklicznie wywoływany tylko raz, wraz z zestawem komend, a\ do
momentu, kiedy lista_słów zostanie wyczerpana.
Powłoka bash umo\liwia tak\e u\ywanie konstrukcji pętli for w formie znanej z języka C:
for (( i=0; <= 5; i++ ))
do
echo $i
done
Konstrukcja while i until
Inny rodzaj pętli stanowią komendy while i until. Blok poleceń objęty działaniem pętli
while jest wykonywany w kolejnych cyklach pętli tak długo, jak długo spełniony jest
warunek określony parametrem, występującym bezpośrednio po słowie while.
Ogólna konstrukcja pętli while, którą obsługuje powłoka bash ma następującą postać:
while komendy1
do
komendy2
done
Opisana konstrukcja powoduje wykonanie listy poleceń, określonym w powy\szym zapisie
jako komendy1 i warunkowe wykonanie poleceń komendy2, objętych działaniem pętli, które
zawarte są pomiędzy słowami kluczowymi do i done. Wykonywane są więc najpierw
polecenia komendy1,a gdy zwrócenie wartości 0 (true) warunkuje to wywołanie instrukcji
zawartych pomiędzy do i done. Pętla jest wykonywana do momentu otrzymywania
wartości 0, w przeciwnym razie jest kończona.
while [ $1 ]
do
echo kod wyjścia: $? argumenty: $*
shift
done
Je\eli podano niepustą wartość parametrów, to konstrukcja while wykonuje test ilości
argumentów, po czym wyświetla aktualną ich liczbę. Dopóki zwracany z testu kod wyjścia
będzie 0 (true) pętla będzie się wykonywała. Zastosowanie wewnętrznego polecenia
powłoki shift w bloku instrukcji uruchamianych po spełnieniu warunku  powodującego
przesuwanie parametrów w lewo i ich kasowanie  zmniejszającego z ka\dym cyklem
liczbę parametrów o jeden i obni\ającego numerację zmiennych, powoduje, \e pętla
będzie wykonywała się do momentu anihilacji ostatniego argumentu, tj. wtedy, gdy test
zwróci wartość ró\ną od zera (w tym wypadku 1).
Połączenie polecenia test z konstrukcją while równoznaczne jest u\yciu konstrukcji if-
then.
Podczas uruchamiania procesów tworzone są tzw. pidfile, czyli pliki informujące o tym,
\e dany proces jest uruchomiony. Zazwyczaj przechowują te\ one numer tego procesu.
Po zakończeniu działania programu, jego pidfile jest automatycznie kasowany.
Przy pomocy konstrukcji while mo\emy sprawdzać czy dane polecenie jeszcze działa i
warunkować wykonywanie kolejnych opartych o wyniki działania poprzednich aplikacji:
while [ -f ~/program1/pidfile ]
do
sleep 10
done
Pętla sprawdza istnienie pliku, jeśli zwrócona jest wartość 0 (true) to czeka 10 sek. i
ponownie dokonuje sprawdzenia. Konstrukcja kończy działania w przypadku skasowania
pliku.
Je\eli potrzebna jest odwrotne działająca pętla, tzn. je\eli komenda ma być wykonana do
momentu otrzymania wartości kodu wyjścia ró\nego od 0 stosujemy konstrukcję until:
until komendy1
do
komendy2
done
Dopóki kod wyjścia komendy1 będzie zwracał wartość wartość ró\ną od 0 blok poleceń
komendy2, będzie wykonywany. Po osiągnięciu wartości 0, pętla zakończy swoje działanie.
until who | grep student1
do sleep 20
done
write student1 << MSG
-- zajecia z 21.01.2012 odwolane --
MSG
Dopóki u\ytkownik nie zaloguje się do systemu pętlą będzie zawieszać działania na 20
sek. po czym uruchomiać się ponownie. Kiedy polecenie zwróci wartość 0 (true) działanie
pętli zostanie przerwane, a znalezionemu w systemie u\ytkownikowi przesłana zostanie
(za pomocą programu write) wiadomość znajdująca się pomiędzy znacznikami << MSG i
MSG.
Zadanie 1
Napisać skrypt, który w zale\ności od tego, czy uruchomił go u\ytkownik student, czy root
wyświetli powitalny napis. Dla u\ytkownika jas i marysia zwróci dwa ró\ne komunikaty, a
dla pozostałych u\ytkowników wypisany będzie napis, \e nie są oni znani skryptowi.
Przetestować działanie skryptu.
Zadanie 2
Napisać skrypt, który przy u\yciu pętli for wyświetli listę 5 dowolnych imion z
poprzedzającym ich stałym tekstem.
Zadanie 3
Napisać skrypt, który ka\de słowo zdania potraktuje, jako osobny element i wypisze go na
standardowym wyjściu: "Lubie "programować" w bash'u".
Zadanie 4
Napisać skrypt o działaniu, jak z zadania 2, z tą ró\nicą, \eby czytanie listy było ze
zmiennej.
Zadanie 5
Napisać skrypt, który przy pomocy pętli będzie wyświetlał wszystkie dane z pliku
"im_nazw", który trzeba uprzednio stworzyć i wprowadzić dowolne dane trzech osób
(ka\da w nowej linii) wg wzoru: Imię Nazwisko
Zadanie 6
Napisać skrypt który sprawdzi i wypisze, które z plików katalogu domowego są katalogami,
a które zwykłymi plikami. Zastosować tylko jedną pętlę.
Zadanie 7
Napisać skrypt, który przy u\yciu pętli for wypisze dziesięć kolejnych wartości.
Zadanie 8
Napisać skrypt, w którym dwie zmienne będą zmieniać wartości przeciwbie\nie: pierwsza
od A do B, a druga od B do A (A i B są cyframi). Zmiana w ka\dym cyklu dla obydwu
zmiennych.
Zadanie 9
Przepisać działanie skryptu z zadania 7 z u\yciem pętli while
Zadanie 10
Napisać skrypt wykonujący to samo zadanie przy u\yciu konstrukcji for
while [ $1 ]
do
echo kod wyjścia: $? argumenty: $*
shift
done
Zadanie 11
Za pomocą konstrukcji until wyliczyć wszystkie wartości z określonym krokiem, od
zadeklarowanej do zera.
Zadanie 12
Napisać skrypt, który dla ka\dej z wartości od 5 do 0 wykona mno\enie ka\dej z tych
wartości przez cyfry od 1 do 3. Wszystkie dane wyświetlić na ekranie i zapisać do pliku z
poziomu skryptu.
Zadanie 13
Napisać skrypt, który dla ka\dej linii pliku /etc/passwd wypisze wszystkie jej elementy .
Przykładowy wynik działania skryptów::
student:x:1000:1004:student:/home/student:/bin/bash
student
x
1000
..
itd.
Zadanie 14
Napisać skrypt, który dla zmiennej tablicowej (stworzyć zmienną składającą się z 7
elementów) wyświetli liczbę elementów przechowujących przez tablicę, a następnie
indeksy i przyporządkowane im wartości elementów, oraz wszystkie elementy nieparzyste.
Zadanie 15
Napisać skrypt, który wypisze nazwy wszystkich plików katalogu domowego małymi
literami, a katalogów du\ymi.


Wyszukiwarka