Jak już widzieliśmy, każda instrukcja awk składa się ze wzorca
i skojarzonej z nim akcji. W tym rozdziale opisano w jaki sposób
konstruuje się wzorce i akcje.
Wzorce w awk sterują wykonywaniem reguł: reguła jest wykonywana
gdy jej wzorzec pasuje do bieżącego rekordu wejściowego. W tej sekcji
zajmujemy się tym, jak pisać wzorce.
Oto podsumowanie typów wzorców obsługiwanych w awk.
/wyrażenie regularne/
wyrażenie
wz1, wz2
BEGIN
END
awk, lub końcowych,
porządkowych przed jej zakończeniem.
(Zob. 8.1.5. Wzorce specjalne BEGIN i END.)
pusty
Już od pierwszych przykładów używaliśmy wyrażeń regularnych jako wzorców. Ten rodzaj wzorca jest po prostu stałym wyrażeniem regularnym w części reguły opisującej wzorzec. Znaczy ono `$0 ~ /wzorzec/'. Taki wzorzec pasuje gdy rekord wejściowy pasuje do wyrażenia regularnego. Na przykład:
/foo|bar|baz/ { belkot++ }
END { print "Wyłapano", belkot, "słów tech-bełkotu" }
Dowolne wyrażenie awk jest poprawnym wzorcem.
Wzorzec ten pasuje jeśli wartość wyrażenia jest niezerowa (jeśli to liczba)
lub niepusta (jeśli łańcuch).
Wyrażenie jest przeliczane przy każdorazowym testowaniu reguły z nowym
rekordem wejściowym. Jeżeli wykorzystuje ono pola, jak np. $1, to
jego wartość zależy wprost od tekstu nowego rekordu. W przeciwnym razie,
zależy tylko od tego, co zdarzyło się do tej pory podczas wykonywania
programu awk, lecz nadal może być użyteczne.
Bardzo często występującym rodzajem wyrażenia stosowanym jako wzorzec jest wyrażenie porównania, korzystające z operatorów porównania opisanych w 7.10. Typy zmiennych i wyrażenia porównania.
Bardzo często stosowanymi wyrażeniami są także dopasowanie i nie-dopasowanie
wyrażenia regularnego. Lewym operandem operatorów `~' i `!~' jest
łańcuch. Prawy operand jest albo stałym wyrażeniem regularnym ujętym
w ukośniki (/regexp/) albo dowolnym innym wyrażeniem, którego
wartość łańcuchowa wykorzystywana jest jako dynamiczne wyrażenie regularne
(zob. 4.7. Stosowanie dynamicznych wyrażeń regularnych).
Poniższy przykład wypisuje drugie pole każdego rekordu wejściowego, którego pierwszym polem jest dokładnie `foo'.
$ awk '$1 == "foo" { print $2 }' BBS-list
(Nie otrzymamy żadnego wyjścia, ponieważ nie ma BBS-u o nazwie "foo".) Inaczej będzie przy poniższym dopasowaniu wyrażenia regularnego, które akceptuje dowolny rekord z pierwszym polem zawierającym `foo':
$ awk '$1 ~ /foo/ { print $2 }' BBS-list
-| 555-1234
-| 555-6699
-| 555-6480
-| 555-2127
Jako wzorce są również powszechnie wykorzystywane wyrażenia logiczne. To, czy rekord pasuje do wzorca, zależy od dopasowania jego podwyrażeń.
Na przykład, poniższe polecenie wypisuje wszystkie rekordy z `BBS-list', które zawierają zarówno `2400' jak i `foo'.
$ awk '/2400/ && /foo/' BBS-list -| fooey 555-1234 2400/1200/300 B
Poniższe polecenie wypisuje wszystkie rekordy z `BBS-list', które zawierają `2400' lub `foo', lub oba.
$ awk '/2400/ || /foo/' BBS-list -| alpo-net 555-3412 2400/1200/300 A -| bites 555-1675 2400/1200/300 A -| fooey 555-1234 2400/1200/300 B -| foot 555-6699 1200/300 B -| macfoo 555-6480 1200/300 A -| sdace 555-3430 2400/1200/300 A -| sabafoo 555-2127 1200/300 C
Poniższe polecenie wypisuje wszystkie rekordy z `BBS-list', które nie zawierają łańcucha `foo'.
$ awk '! /foo/' BBS-list -| aardvark 555-5553 1200/300 B -| alpo-net 555-3412 2400/1200/300 A -| barfly 555-7685 1200/300 A -| bites 555-1675 2400/1200/300 A -| camelot 555-0542 300 C -| core 555-2912 1200/300 C -| sdace 555-3430 2400/1200/300 A
Podwyrażenia operatora logicznego we wzorcu mogą być wyrażeniami
regularnymi, porównaniami, czy dowolnymi innymi wyrażeniami awk.
Wzorce zakresu nie są wyrażeniami, więc nie mogą pojawić się wewnątrz
wzorców logicznych. Podobnie, nie są wyrażeniami i nie mogą pojawić się
wewnątrz wzorców logicznych wzorce specjalne BEGIN i END,
które nigdy nie dopasowują żadnego rekordu wejściowego.
Specjalnym przypadkiem wyrażenia będącego wzorcem wzorca jest też stałe
wyrażenie regularne. /foo/ jako wyrażenie ma wartość jeden jeśli w
rekordzie wejściowym pojawia się `foo'. Zatem, jako wzorzec,
/foo/ dopasowuje dowolny rekord zawierający `foo'.
Wzorzec zakresu tworzą dwa wzorce rozdzielone przecinkiem: ma on postać `wzpocz, wzkońc'. Dopasowuje zakres kolejnych wierszy wejściowych. Pierwszy wzorzec, wzpocz, decyduje o tym, gdzie zaczyna się zakres, a drugi, wzkońc, gdzie się on kończy. Na przykład,
awk '$1 == "on", $1 == "off"'
wypisuje każdy rekord wejściowy pomiędzy parami `on'/`off', z nimi włącznie.
Wzorzec zakresu zaczyna pracę od dopasowywania do wzpocz każdego rekordu wejściowego. Kiedy rekord pasuje do wzpocz, zakres rekordów staje się włączony. Ten rekord pasuje do zakresu rekordów. Dopóki zakres pozostaje włączony, automatycznie pasuje do niego każdy przeczytany rekord wejściowy. Równocześnie każdy z nich jest dopasowywany do wzkońc; gdy dopasowanie się powiedzie, wzorzec zakresu jest ponownie wyłączany przed następnym rekordem. Następnie powraca do sprawdzania każdego rekordu z wzpocz.
Zarówno rekord, który włączył wzorzec, jak i rekord, który go wyłączył
pasują do wzorca zakresu. Jeżeli nie chcemy działać na tych rekordach,
możemy w akcji danej reguły napisać instrukcje if, odróżniające
je od rekordów, którymi jesteśmy zainteresowani.
Możliwe jest, że wzorzec zostanie włączony i wyłączony przez ten sam rekord, jeżeli spełnia on oba warunki. Wówczas skojarzona ze wzorcem akcja wykonywana jest tylko dla tego rekordu.
Na przykład, załóżmy, że mamy tekst między dwoma identycznymi znacznikami
(powiedzmy, symbolami `%'), który chcemy zignorować. Można usiłować
połączyć wzorzec zakresu, opisujący ograniczany tekst, z instrukcją
next (jeszcze nie omawianą,
zob. 9.7. Instrukcja next),
co spowoduje, że awk pominie dalsze przetwarzanie bieżącego rekordu
i rozpocznie od nowa z nowym rekordem wejściowym. Program taki mógłby
wyglądać tak:
/^%$/,/^%$/ { next }
{ print }
Ten program nie działa poprawnie, gdyż wzorzec zakresu jest zarówno włączany jak i wyłączany przez pierwszy wiersz z samym znakiem `%'. Do realizacji naszego zadania musimy napisać program w taki sposób, korzystając z flagi:
/^%$/ { skok = ! skok; next }
skok == 1 { next } # przeskocz wiersze gdy ustawiony `skok'
Zauważ, że przecinek `,' we wzorcu zakresu ma najniższy priorytet (jest obliczany ostatni) ze wszystkich operatorów. Zatem, na przykład, poniższy program usiłuje połączyć wzorzec zakresu z innym, prostszym testem.
echo Tak | awk '/1/,/2/ || /Tak/'
Autor programu chciał, by znaczyło to `(/1/,/2/) || /Tak/'.
Jednak awk interpretuje je jako `/1/, (/2/ || /Tak/)'.
Zachowania tego nie można zmienić lub obejść; wzorce zakresu nie łączą się
z innymi wzorcami.
BEGIN i END
BEGIN i END są wzorcami specjalnymi. Nie są używane do
dopasowania rekordów wejściowych. Zamiast tego, umożliwiają podanie
akcji startowych lub końcowych dla własnego skryptu awk.
Reguła BEGIN wykonywana jest, jednokrotnie, przed odczytaniem
pierwszego rekordu wejściowego. Reguła END wykonywana jest,
jednokrotnie, po przeczytaniu całości wejścia. Na przykład:
$ awk '
> BEGIN { print "Analiza \"foo\"" }
> /foo/ { ++n }
> END { print "\"foo\" występuje " n " razy." }' BBS-list
-| Analiza "foo"
-| "foo" występuje 4 razy.
Ten program znajduje liczbę rekordów pliku wejściowego `BBS-list'
zawierających łańcuch `foo'. Reguła BEGIN wypisuje tytuł
raportu. Nie ma potrzeby wykorzystywania reguły BEGIN do inicjowania
licznika n na zero, gdyż awk robi to automatycznie
(zob. 7.3. Zmienne).
Druga reguła zwiększa zmienną n za każdym razem, gdy zostanie
przeczytany rekord zawierający wzorzec `foo'. Reguła END
na koniec pracy programu wypisuje wartość n.
Wzorce specjalne BEGIN i END nie mogą być stosowane
w zakresach ani z operatorami logicznymi (faktycznie nie mogą być używane
z żadnymi operatorami).
Program awk może mieć wiele reguł BEGIN i/lub END.
Są one wykonywane w kolejności występowania, wszystkie reguły BEGIN
przy rozpoczęciu pracy a wszystkie reguły END przy zakończeniu.
Reguły BEGIN i END mogą być przeplatane z innymi regułami.
Własność tę dodano w awk w wersji z roku 1987 i jest zawarta
w standardzie POSIX. Pierwotna wersja awk (z 1978 roku) wymagała
umieszczania reguły BEGIN na samym początku programu a END
na końcu i zezwalała tylko na jedną regułą BEGIN i jedną END.
Nie jest to już wymagane, ale jest dobrym rozwiązaniem jeśli chodzi o
organizację programu i jego czytelność.
Wielokrotne reguły BEGIN i END są przydatne przy pisaniu
funkcji bibliotecznych, gdyż każdy plik biblioteczny może mieć swoją
własną regułę BEGIN i/lub END do wykonania własnej
inicjalizacji i/lub porządkowania. Warto zauważyć, że kolejność, w jakiej
wymieniane są funkcje biblioteczne w wierszu poleceń wyznacza kolejność,
w jakiej zostaną wykonane ich reguły BEGIN i END. Z tego
powodu reguły te w plikach bibliotecznych muszą być pisane tak, by kolejność
ich wykonywania nie miała znaczenia.
Zob. 14.1. Opcje wiersza poleceń, gdzie jest więcej o stosowaniu
funkcji bibliotecznych.
Zob. 15. Biblioteczka funkcji awk, o wielu
przydatnych funkcjach bibliotecznych.
Jeżeli program awk ma tylko jedną regułę BEGIN
i żadnych innych reguł, to program kończy pracę po wykonaniu reguły
BEGIN. (Pierwotna wersja awk kontynuowała czytanie
i ignorowanie wejścia aż do osiągnięcia końca pliku.) Jeśli jednak istnieje
reguła END, to wejście będzie czytane, nawet jeżeli w programie nie
ma żadnych innych reguł. Jest to konieczne na wypadek, gdyby reguła
END sprawdzała zmienne FNR i NR (c.k.).
Reguły BEGIN i END muszą mieć akcje. Nie istnieje dla
nich żadna akcja domyślna, gdyż podczas ich pracy nie ma żadnego rekordu
bieżącego.
BEGIN i END
Istnieje kilka (czasem trudno uchwytnych) kwestii związanych z wykonywaniem
operacji wejścia/wyjścia w regule BEGIN lub END.
Pierwsza ma związek z wartością $0 w regule BEGIN. Ponieważ
reguły BEGIN są wykonywane przed odczytem jakiegokolwiek wejścia,
to podczas ich wykonywania po prostu ma żadnego rekordu wejściowego,
a więc i żadnych pól. Odwołania do $0 i pól zwracają łańcuch pusty
lub zero, zależnie od kontekstu. Jedną z metod nadania $0
rzeczywistej wartości jest wykonanie polecenia getline bez zmiennej
(zob. 5.8. Odczyt bezpośredni przez getline). Inną metodą jest
po prostu przypisanie jej wartości.
Druga istotna sprawa jest zbliżona do pierwszej, tylko dotyczy
odwrotnego kierunku. Jakie są wartości $0 i NF wewnątrz
reguły END? Tradycyjnie, z powodu głównie kwestii implementacyjnych,
$0 i NF wewnątrz reguły END były
niezdefiniowane. Standard POSIX określił, że NF jest dostępne
w regule END, i zawiera liczbę pól z ostatniego rekordu wejściowego.
Najprawdopodobniej wskutek przeoczenia, standard nie mówi, że zachowywane
jest również $0, choć logicznie myśląc tak powinno być.
Faktycznie, gawk zachowuje wartość $0 do wykorzystania
w regułach END. Należy jednak być świadomym, że uniksowy awk,
i być może inne implementacje, tego nie robią.
Trzecia kwestia wynika z dwu pierwszych. Jak należy rozumieć `print'
wewnątrz reguły BEGIN lub END? Znaczy ono zawsze to samo,
`print $0'. Jeżeli $0 jest łańcuchem pustym, to wypisze pusty
wiersz. Wielu z dawna piszących w awk programistów stosuje
`print' w regułach BEGIN i END w znaczeniu
`print ""', polegając na tym, że $0 jest puste.
Mimo, że ogólnie można tego uniknąć w regułach BEGIN, przynajmniej
w gawk, to jest to bardzo złym rozwiązaniem w regułach END.
Świadczy też o kiepskim stylu programowania, ponieważ jeżeli chcemy
pustego wiersza na wyjściu, powinniśmy w programie zapisać to wprost.
Pusty (tj. nieistniejący) wzorzec jest uważany za dopasowujący każdy rekord wejściowy. Na przykład, program:
awk '{ print $1 }' BBS-list
wypisuje pierwsze pole każdego rekordu.
Program lub skrypt awk składa się z szeregu przeplecionych reguł
i definicji funkcji.
(Funkcje są opisane dalej. Zob. 13. Funkcje definiowane przez użytkownika.)
Reguła zawiera wzorzec i akcję, z których każde (ale nie oba równocześnie)
może być pominięte. Celem akcji jest przekazanie awk, co ma
zrobić po znalezieniu dopasowania do wzorca. Zatem, w zarysie, program
awk ogólnie wygląda tak:
[wzorzec] [{ akcja }]
[wzorzec] [{ akcja }]
...
function nazwa(argumenty) { ... }
...
Akcja składa się z jednej lub więcej instrukcji awk, zawartych
w nawiasach klamrowych (`{' i `}'). Każda instrukcja określa
jedną rzecz do wykonania. Instrukcje oddzielane są znakami nowej linii
lub średnikami.
Nawiasy klamrowe wokół akcji muszą być użyte nawet jeśli akcja zawiera tylko jedną instrukcję, a nawet jeśli w ogóle nie zawiera instrukcji. Jednak, jeżeli całkowicie pomijamy akcję, należy również pominąć nawiasy klamrowe. Pominięta akcja jest równoważna `{ print $0 }'.
/foo/ { } # dopasowuje foo, nic nie robi - akcja pusta
/foo/ # dopasowuje foo, wypisuje rekord - pominięta akcja
A oto rodzaje instrukcji obsługiwane przez awk:
awk.
Język awk udostępnia konstrukty C-podobne
(if, for, while i do), jak i kilka specjalnych
(zob. 9. Instrukcje sterujące w akcjach).
if, while, do lub for
kilku instrukcji razem.
getline (zob. 5.8. Odczyt bezpośredni przez getline),
instrukcji next (zob. 9.7. Instrukcja next),
i instrukcji
nextfile (zob. 9.8. Instrukcja nextfile).
print i printf.
Zob. 6. Wypisywanie wyjścia.
delete.
Następny rozdział omawia szczegółowo instrukcje sterujące.
Przejdź do pierwszej, poprzedniej, następnej, ostatniej sekcji, spisu treści.