Home Dokumentacje Efektywne programowanie w AWK - Podręcznik użytkownika GNU awk - Wzorce i akcje
18 | 08 | 2019
Efektywne programowanie w AWK - Podręcznik użytkownika GNU awk - Wzorce i akcje Drukuj

Przejdź do pierwszej, poprzedniej, następnej, ostatniej sekcji, spisu treści.

 


 

8. Wzorce i akcje

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.

8.1. Elementy wzorców

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.

8.1.1. Rodzaje wzorców

Oto podsumowanie typów wzorców obsługiwanych w awk.

/wyrażenie regularne/
Wyrażenie regularne jako wzorzec. Pasuje gdy tekst bieżącego rekordu wejściowego pasuje do wyrażenia regularnego. (Zob. 4. Wyrażenia regularne.)
wyrażenie
Pojedyncze wyrażenie. Pasuje gdy jego wartość jest niezerowa (jeśli to liczba) lub niepusta (jeśli łańcuch). (Zob. 8.1.3. Wyrażenia jako wzorce.)
wz1, wz2
Para wzorców rozdzielona przecinkiem, określająca zakres rekordów. Zakres zawiera zarówno rekord początkowy, który pasuje do wz1, jak i rekord końcowy, pasujący do wz2. (Zob. 8.1.4. Określanie zakresów rekordów za pomocą wzorców.)
BEGIN
END
Wzorce specjalne umożliwiające podanie akcji początkowych, wykonywanych przy rozpoczęciu pracy programu awk, lub końcowych, porządkowych przed jej zakończeniem. (Zob. 8.1.5. Wzorce specjalne BEGIN i END.)
pusty
Wzorzec pusty. Pasuje do niego każdy rekord wejściowy. (Zob. 8.1.6. Wzorzec pusty.)

8.1.2. Wyrażenia regularne jako wzorce

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" }

8.1.3. Wyrażenia jako wzorce

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'.

8.1.4. Określanie zakresów rekordów za pomocą wzorców

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.

8.1.5. Wzorce specjalne 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.

8.1.5.1. Akcje początkowe i końcowe

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.

8.1.5.2. Wejście/wyjście z reguł 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.

8.1.6. Wzorzec pusty

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.

8.2. Przegląd akcji

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:

  • Wyrażenia, które mogą wywoływać funkcje lub przypisywać wartości zmiennym (zob. 7. Wyrażenia). Wykonanie tego rodzaju instrukcji oblicza po prostu wartość wyrażenia. Przydatne gdy wyrażenie ma skutki uboczne. (zob. 7.7. Wyrażenia przypisania).
  • Instrukcje sterujące, określające przebieg sterowania programów 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).
  • Instrukcje złożone, składające się z jednej lub więcej instrukcji ujętych w nawiasy klamrowe. Instrukcję złożoną stosuje się w celu umieszczenia w ciele instrukcji if, while, do lub for kilku instrukcji razem.
  • Instrukcje wejścia, korzystające z polecenia getline (zob. 5.8. Odczyt bezpośredni przez getline), instrukcji next (zob. 9.7. Instrukcja next), i instrukcji nextfile (zob. 9.8. Instrukcja nextfile).
  • Instrukcje wyjścia, print i printf. Zob. 6. Wypisywanie wyjścia.
  • Instrukcje usuwania, do usuwania elementów tablic. Zob. 11.6. Instrukcja delete.

Następny rozdział omawia szczegółowo instrukcje sterujące.

 


Przejdź do pierwszej, poprzedniej, następnej, ostatniej sekcji, spisu treści.

 
Linki sponsorowane

W celu realizacji usług i funkcji na witrynach internetowych ZUI "ELPRO" stosujemy pliki cookies. Korzystanie z witryny bez zmiany ustawień dotyczących plików cookies oznacza, że będą one zapisywane w urządzeniu wyświetlającym stronę internetową. Więcej szczegółów w Polityce plików cookies.

Akceptuję pliki cookies z tej witryny.