Instrukcje sterujące jak if, while, i tak dalej sterują
przebiegiem wykonania programów awk. Większość instrukcji
sterujących w awk wzorowana jest na podobnych instrukcjach z C.
Wszystkie instrukcje sterujące zaczynają się specjalnym słowem kluczowym,
jak np. if czy while, które odróżnia je od wyrażeń prostych.
Wiele instrukcji sterujących zawiera inne instrukcje. Na przykład,
instrukcja if zawiera inną instrukcję, która może być wykonana
bądź nie. Taka zawarta instrukcja zwana jest ciałem (body) instrukcji
sterującej. Jeżeli w ciele instrukcji sterującej chcemy zawrzeć więcej
niż jedną instrukcję, to grupujemy je, za pomocą nawiasów klamrowych,
w pojedynczą instrukcję złożoną, oddzielając je od siebie znakami
nowej linii lub średnikami.
if-else
Instrukcja if-else jest w awk instrukcją "podejmowania
decyzji". Wygląda tak:
if (warunek) ciało-if [else ciało-else]
Warunek jest wyrażeniem, które decyduje o tym, co zrobi reszta
instrukcji. Jeżeli warunek jest prawdziwy, to wykonywane jest
ciało-if; w przeciwnym razie, wykonywane jest ciało-else.
Część else instrukcji jest opcjonalna. Warunek jest uważany za
fałszywy jeśli jego wartością jest zero lub łańcuch pusty, w przeciwnym
wypadku za prawdziwy.
Oto przykład:
if (x % 2 == 0)
print "x jest parzyste"
else
print "x jest nieparzyste"
W tym przykładzie, jeśli wyrażenie `x % 2 == 0' jest prawdziwe (to
jest, wartość x dzieli się przez dwa bez reszty), to wykonywana jest
pierwsza instrukcja print, w przeciwnym razie wykonywana jest druga
instrukcja print.
Jeśli else pojawia się w tym samym wierszu, co ciało-if,
a ciało-if nie jest instrukcją złożoną (tj. nie jest otoczone
nawiasami klamrowymi), to ciało-if musi być oddzielone od else
średnikiem. Dla ilustracji, napiszmy na nowo poprzedni przykład:
if (x % 2 == 0) print "x jest parzyste"; else
print "x jest nieparzyste"
Jeżeli zapomnimy średnika `;', to awk nie będzie w stanie
zinterpretować instrukcji i otrzymamy błąd składniowy.
W rzeczywistości powyższego przykładu nie napisalibyśmy w ten sposób,
gdyż czytelnik mógłby nie zauważyć else gdyby nie było pierwszą
rzeczą w wierszu.
whileW programowaniu słowo pętla (loop) oznacza część programu, która może być wykonana kolejno dwa lub więcej razy z rzędu.
Instrukcja while jest najprostszą instrukcją pętli w awk.
Powtarza wykonywanie instrukcji dopóki warunek jest prawdziwy. Wygląda tak:
while (warunek) ciało
gdzie ciało jest instrukcją, którą nazywamy ciałem pętli, zaś warunek jest wyrażeniem decydującym o tym, jak długo ma działać pętla.
Pierwszą rzeczą, jaką robi instrukcja while jest sprawdzenie
warunku. Jeżeli jest prawdziwy, to wykonuje ona instrukcję
ciało.
Po wykonaniu ciała pętli warunek jest testowany ponownie
i jeżeli nadal jest prawdziwy, powtórnie wykonywane jest ciało.
Proces ten powtarza się aż warunek przestanie być prawdziwy.
Jeżeli warunek jest początkowo fałszywy, to ciało funkcji nie
jest nigdy wykonywane, a awk kontynuuje pracę używając kolejnej
po pętli instrukcji.
Ten przykład wypisuje pierwsze trzy pola każdego rekordu, po jednym w wierszu.
awk '{ i = 1
while (i <= 3) {
print $i
i++
}
}' inventory-shipped
Tu ciałem pętli jest ujęta w nawiasy klamrowe instrukcja złożona, zawierająca dwie instrukcje.
Nasza pętla działa tak: najpierw do i przypisywane jest jeden.
Następnie, while sprawdza, czy i jest mniejsze lub równe trzy.
Jest to prawdą gdy i równa się jeden, więc wypisywane jest i-te
pole. Potem `i++' zwiększa wartość i o jeden i pętla się
powtarza. Pętla kończy pracę, gdy i dojdzie do czterech.
Jak widać, pomiędzy warunkiem a ciałem nie jest wymagany znak nowej linii, ale zastosowanie go czyni program czytelniejszym, chyba że ciało jest instrukcją złożoną lub jest bardzo proste. Znak nowej linii po otwierającym nawiasie klamrowym, który rozpoczyna instrukcję złożoną, także nie jest konieczny, ale bez niego program byłby trudniejszy do czytania.
do-while
Pętla do jest odmianą instrukcji pętli while.
Instrukcja do wykonuje jednokrotnie ciało, a następnie
powtarza jego wykonywanie dopóki warunek jest prawdziwy. Wygląda tak:
do ciało while (warunek)
Nawet jeżeli warunek jest fałszywy na starcie, ciało zostanie
wykonane co najmniej raz (i tylko raz, chyba że wykonanie go spowoduje, że
warunek stanie się prawdziwy). Inaczej jest z odpowiednią instrukcją
while:
while (warunek) ciało
Ta instrukcja nie wykona ciała ani razu jeśli warunek, z którym rozpoczyna jest fałszywy.
Oto przykład instrukcji do:
awk '{ i = 1
do {
print $0
i++
} while (i <= 10)
}'
Ten program wypisuje każdy rekord dziesięć razy. Nie jest to zbyt
realistyczny przykład, gdyż w tym przypadku równie dobrze wystarczyłoby
zwykłe while.
Odzwierciedla to rzeczywistą praktykę: faktyczna potrzeba użycia
do występuje tylko sporadycznie.
for
Instrukcja for ułatwia zliczanie iteracji pętli. Ogólna postać
instrukcji for wygląda tak:
for (inicjalizacja; warunek; inkrement) ciało
Części inicjalizacja, warunek i inkrement są dowolnymi
wyrażeniami awk, zaś ciało oznacza dowolną instrukcję
awk.
Instrukcja for zaczyna pracę od wykonania inicjalizacji.
Następnie, dopóki warunek jest prawdziwy, powtarza wykonywanie
ciała a potem inkrementu. Typowo inicjalizacja nadaje
pewnej zmiennej wartość zero lub jeden, inkrement dodaje do niej
jeden, a warunek porównuje ją z pożądaną liczbą iteracji.
Oto przykład instrukcji for:
awk '{ for (i = 1; i <= 3; i++)
print $i
}' inventory-shipped
Wypisuje on pierwsze trzy pola każdego rekordu wejściowego, po jednym polu w wierszu.
W części inicjalizacja nie można nadać wartości więcej niż jednej
zmiennej, chyba że posłużymy się przypisaniem wielokrotnym, jak np.
`x = y = 0', które jest możliwe tylko gdy wszystkie wartości
początkowe są równe. (Można jednak zainicjować dodatkowe zmienne pisząc
przypisania do nich jako osobne instrukcje przed pętlą for.)
Obowiązuje to także dla części inkrement; chcąc zwiększać dodatkowe
zmienne, musimy napisać osobne instrukcje na końcu pętli. W tym kontekście
przydatne byłoby wyrażenie złożone z C, używające separatora przecinkowego,
ale nie jest ono rozpoznawane w awk.
Najczęściej inkrement jest wyrażeniem inkrementującym, jak w przykładzie powyżej. Nie jest to jednak wymagane; może to być jakiekolwiek wyrażenie dowolnego typu. Na przykład, ta instrukcja wypisuje wszystkie potęgi dwójki między jeden a 100:
for (i = 1; i <= 100; i *= 2) print i
Można pominąć dowolne z trzech wyrażeń występujących w nawiasach
po for jeśli ma ono nic nie robić. Zatem, `for (; x > 0;)'
jest równoważne `while (x > 0)'. Jeżeli pominięto warunek,
jest on traktowany jak prawda (true), w rezultacie powodując
nieskończoną pętlę (tj. pętlę, która nigdy nie zakończy pracy).
W większości przypadków pętla for jest skrótem pętli while,
jak pokazano tutaj:
inicjalizacja
while (warunek) {
ciało
inkrement
}
Jedyny wyjątek stanowi sytuacja, gdy wewnątrz pętli zastosowano instrukcję
continue
(zob. 9.6. Instrukcja continue).
Zmiana w podany sposób instrukcji for na while może
zmienić skutki instrukcji continue wewnątrz pętli.
Istnieje alternatywna wersja pętli loop przeznaczona do przechodzenia
kolejno po wszystkich indeksach tablicy:
for (i in tablica)
zrób coś z tablica[i]
Zob. 11.5. Przeglądanie wszystkich elementów tablicy,
gdzie jest więcej o tej wersji pętli for.
Język awk oprócz instrukcji while ma instrukcję
for, ponieważ często pętla for jest mniej pracochłonna przy
wpisywaniu i bardziej naturalna w myśleniu o niej.
W pętlach zliczanie liczby iteracji jest bardzo częste. Łatwiej jest
myśleć o zliczaniu jako o części pętli niż jako o czymś, co ma być zrobione
wewnątrz niej.
Następna sekcja zawiera bardziej skomplikowane przykłady pętli for.
break
Instrukcja break wyskakuje z najbardziej wewnętrznej obejmującej ją
pętli for, while lub do. Poniższy program znajduje
najmniejszy dzielnik liczby całkowitej, rozpoznaje też liczby pierwsze:
awk '# znajdź najmniejszy dzielnik liczby num
{ num = $1
for (div = 2; div*div <= num; div++)
if (num % div == 0)
break
if (num % div == 0)
printf "Najmniejszym dzielnikiem %d jest %d\n", num, div
else
printf "%d jest liczbą pierwszą\n", num
}'
Gdy resztą z dzielenia w pierwszej instrukcji if jest zero,
awk natychmiast przerywa (breaks out) działanie
zawierającej ją pętli for. Oznacza to, że awk przechodzi
bezzwłocznie do instrukcji następującej po pętli i kontynuuje przetwarzanie.
(Jest to całkiem inne niż instrukcja exit, która zatrzymuje cały
program awk.
Zob. 9.9. Instrukcja exit.)
Oto inny program równoważny poprzedniemu. Ilustruje sposób, w jaki
warunek pętli for lub while może być równie dobrze
zastąpiony przez break wewnątrz if:
awk '# znajdź najmniejszy dzielnik liczby num
{ num = $1
for (div = 2; ; div++) {
if (num % div == 0) {
printf "Najmniejszym dzielnikiem %d jest %d\n", num, div
break
}
if (div*div > num) {
printf "%d jest liczbą pierwszą\n", num
break
}
}
}'
Jak opisano powyżej, instrukcja break nie ma żadnego znaczenia, jeśli
użyta jest poza ciałem pętli. Jednak, mimo iż nigdy tego nie
udokumentowano, historyczne implementacje awk traktowały break
poza pętlą tak, jakby była to instrukcja next
(zob. 9.7. Instrukcja next).
Najnowsze wersje uniksowego awk nie pozwalają już na taki sposób
użycia. gawk obsługuje takie użycie break tylko jeśli
w wierszu poleceń podano opcję `--traditional'
(zob. 14.1. Opcje wiersza poleceń).
W przeciwnym wypadku, zostanie ono potraktowane jako błąd, gdyż standard
POSIX określa, że break powinno być stosowane wyłącznie wewnątrz
ciała pętli (c.k.).
continue
Instrukcja continue, podobnie jak break, używana jest tylko
wewnątrz pętli for, while i do. Pomija ona resztę
ciała pętli, powodując natychmiastowe rozpoczęcie kolejnego cyklu pętli.
Zwróć uwagę na różnicę w stosunku do break, które całkowicie
wyskakuje z pętli.
Instrukcja continue w pętli for nakazuje awk
przeskoczenie reszty ciała pętli i wznowienie jej wykonywania od
wyrażenia inkrementacji instrukcji for.
Ten fakt ilustruje poniższy program:
awk 'BEGIN {
for (x = 0; x <= 20; x++) {
if (x == 5)
continue
printf "%d ", x
}
print ""
}'
Program wypisuje wszystkie liczby od zera do 20, za wyjątkiem piątki,
dla której printf jest pomijane. Ponieważ nie jest pomijany
inkrement `x++', x nie pozostanie zaklinowane na pięciu.
Inaczej niż w pętli loop powyżej jest w tej pętli while:
awk 'BEGIN {
x = 0
while (x <= 20) {
if (x == 5)
continue
printf "%d ", x
x++
}
print ""
}'
Ten program od momentu, gdy x otrzyma wartość pięć, będzie wykonywał
wieczną pętlę.
Jak opisano powyżej, instrukcja continue nie ma żadnego znaczenia,
jeśli użyta jest poza ciałem pętli. Jednak, mimo iż nigdy tego nie
udokumentowano, historyczne implementacje awk traktowały
continue poza pętlą tak, jakby była to instrukcja next
(zob. 9.7. Instrukcja next).
Najnowsze wersje uniksowego awk nie pozwalają już na taki sposób
użycia. gawk obsługuje takie użycie continue tylko jeśli
w wierszu poleceń podano opcję `--traditional'
(zob. 14.1. Opcje wiersza poleceń).
W przeciwnym wypadku, zostanie ono potraktowane jako błąd, gdyż standard
POSIX określa, że continue powinno być stosowane wyłącznie wewnątrz
ciała pętli (c.k.).
next
Instrukcja next wymusza na awk natychmiastowe przerwanie
przetwarzania bieżącego rekordu i przejście do następnego. Znaczy to, że
dla bieżącego rekordu nie będą wykonywane żadne dalsze reguły.
Nie będzie też wykonywana reszta akcji aktualnej reguły.
Różni się to od skutków funkcji getline
(zob. 5.8. Odczyt bezpośredni przez getline).
getline również powoduje, że awk odczytuje natychmiast kolejny
rekord, ale nie zmienia w żaden sposób przebiegu sterowania. Tak więc,
z nowym rekordem wejściowym wykonywana jest dalsza część bieżącej akcji.
Na najwyższym poziomie wykonanie programu awk jest pętlą, która
czyta rekord wejściowy i sprawdza go ze wzorcem każdej reguły.
Jeśli myślimy o tej pętli jak o instrukcji for, której ciało zawiera
reguły, to instrukcja next jest analogiczna do continue:
przeskakuje na koniec ciała tej niejawnej pętli i wykonuje inkrementację
(która czyta kolejny rekord).
Na przykład, jeżeli program awk działa tylko na rekordach
o czterech polach, i nie chcemy by pracował błędnie gdy otrzyma złe
wejście, możemy wykorzystać taką regułę blisko początku programu:
NF != 4 {
err = sprintf("%s:%d: pominięty: NF != 4\n", FILENAME, FNR)
print err > "/dev/stderr"
next
}
tak, że kolejne reguły nie otrzymają nieprawidłowego rekordu. Komunikat o
błędzie przekierowywany jest do standardowego strumienia błędów,
tak jak powinny być kierowane komunikaty o błędach.
Zob. 6.7. Specjalne nazwy plików w gawk.
Zgodnie ze standardem POSIX, jeśli instrukcja next jest użyta
w regule BEGIN lub END to zachowanie się programu jest
niezdefiniowane.
gawk będzie traktował taką sytuację jako błąd składniowy.
Mimo, że POSIX na to zezwala, niektóre inne implementacje awk
nie pozwalają na umieszczanie instrukcji next wewnątrz ciała funkcji
(zob. 13. Funkcje definiowane przez użytkownika).
Tak jak każda inna instrukcja next, next we wnętrzu
ciała funkcji czyta następny rekord i rozpoczyna jego przetwarzanie
za pomocą pierwszej reguły programu.
Jeżeli instrukcja next spowoduje osiągnięcie końca wejścia,
to zostanie wykonany kod z ewentualnych reguł END.
Zob. 8.1.5. Wzorce specjalne BEGIN i END.
Uwaga! Niektóre implementacje awk generują błąd
wykonania jeśli użyjemy instrukcji next wewnątrz funkcji
zdefiniowanej przez użytkownika
(zob. 13. Funkcje definiowane przez użytkownika).
gawk nie ma takiego problemu.
nextfile
gawk udostępnia instrukcję nextfile, która jest zbliżona do
instrukcji next. Jednak, zamiast zaprzestania przetwarzania bieżącego
rekordu, instrukcja nextfile rozkazuje gawk przerwanie
przetwarzania bieżącego pliku danych.
Podczas wykonania instrukcji nextfile, FILENAME aktualizowane
jest nazwą następnego podanego w wierszu poleceń pliku danych, FNR
ponownie otrzymuje początkową wartość jeden, zwiększane jest ARGIND,
a przetwarzanie rozpoczyna się od nowa od pierwszej reguły programu.
Zob. 10. Zmienne wbudowane.
Jeżeli nextfile spowoduje osiągnięcie końca wejścia,
to zostanie wykonany kod ewentualnych reguł END.
Zob. 8.1.5. Wzorce specjalne BEGIN i END.
Instrukcja nextfile jest rozszerzeniem gawk; nie jest ona
(obecnie) dostępna w żadnej innej implementacji awk.
Zob. 15.2. Implementacja nextfile jako funkcji,
gdzie podano funkcję definiowaną przez użytkownika, jaką można wykorzystać
do symulacji instrukcji nextfile.
Instrukcja nextfile może być użyteczna jeśli mamy do przetworzenia
wiele plików danych, a spodziewamy się, że nie będziemy chcieć
przetwarzać każdego rekordu w każdym pliku. Normalnie, w celu przejścia do
następnego pliku danych, musielibyśmy kontynuować przeglądanie niepożądanych
rekordów. Instrukcja nextfile realizuje to znacznie efektywniej.
Uwaga! Wersje gawk wcześniejsze niż 3.0 używały dwu
słów (`next file') do zapisania instrukcji nextfile.
W wersji 3.0 zmieniono to na jedno słowo, gdyż traktowanie słowa `file'
było niespójne. Gdy pojawiało się po next, było słowem kluczowym.
W przeciwnym razie, było zwykłym identyfikatorem. Stara składnia jest nadal
akceptowana. gawk wygeneruje jednak komunikat ostrzegawczy,
a obsługa next file ostatecznie przestanie być kontynuowana
w przyszłych wersjach awk.
exit
Instrukcja exit powoduje, że awk natychmiast przestaje
wykonywać bieżącą regułę i przestaje przetwarzać dane wejściowe; wszelkie
pozostałe wejście jest ignorowane. Wygląda tak:
exit [kod powrotu]
Jeśli instrukcja exit zostanie wykonana z reguły BEGIN,
to program natychmiast wstrzymuje wszelkie przetwarzanie. Nie są czytane
żadne rekordy wejściowe. Jednak, jeśli istnieje reguła END,
to jest ona wykonywana.
(zob. 8.1.5. Wzorce specjalne BEGIN i END).
Jeżeli exit użyte jest jako część reguły END, powoduje
natychmiastowe zatrzymanie programu.
Instrukcja exit nie będąca częścią ani reguły BEGIN ani
END wstrzymuje wykonywanie ewentualnych dalszych automatycznych
reguł dla bieżącego rekordu, pomija odczyt pozostałych rekordów wejściowych,
i wykonuje regułę END, jeśli taka istnieje.
Jeżeli nie chcemy, by w takim przypadku reguła END wykonała swe
zadanie, możemy przed instrukcją exit przypisać jakiejś zmiennej
wartość niezerową i sprawdzić tę zmienną w regule END.
Zob. 15.3. Asercje, gdzie jest przykład, który to robi.
Jeżeli podano argument instrukcji exit, to jego wartość jest
wykorzystywana jako kod zakończenia procesu awk. Jeśli nie podano
argumentu, exit zwraca kod zero (powodzenie). W przypadku, gdy
podano argument pierwszej instrukcji exit, a następnie wywołano
exit po raz drugi bez argumentu, używana jest poprzednio podana
wartość zakończenia (c.k.).
Na przykład, powiedzmy, że wykryliśmy warunek wystąpienia błędu, który
naprawdę nie wiemy jak obsłużyć. Zwyczajowo programy zgłaszają
taką sytuację kończąc pracę z niezerowym kodem. Nasz program awk
może to robić korzystając z instrukcji exit z niezerowym argumentem.
Oto przykład:
BEGIN {
if (("date" | getline date_now) <= 0) {
print "Nie mogę pobrać daty systemowej" > "/dev/stderr"
exit 1
}
print "bieżąca data to", date_now
close("date")
}
Przejdź do pierwszej, poprzedniej, następnej, ostatniej sekcji, spisu treści.