AWK

AWK – interpretowany język programowania, którego główną funkcją jest wyszukiwanie i przetwarzanie wzorców w plikach lub strumieniach danych. Jest także nazwą programu początkowo dostępnego dla systemów operacyjnych będących pochodnymi UNIX-a, obecnie także na inne platformy.

AWK jest językiem, który w znacznym stopniu wykorzystuje tablice asocjacyjne, stringi i wyrażenia regularne. Nazwa języka pochodzi od pierwszych liter nazwisk jego autorów Alfreda V. Aho, Petera Weinbergera i Briana Kernighana. Bywa zapisywana małymi literami, odczytywana jako jedno słowo awk, wymawiana jak pierwsza sylaba w awkward.

Definicja języka AWK jest zawarta w POSIX 1003.2 Command Language And Utilities Standard. Wersja ta jest z kolei oparta na opisie z The AWK Programming Language napisanym przez Aho, Weinbergera i Kernighana, z dodatkowymi właściwościami zdefiniowanymi w wersji awk z SVR4.

W wierszu poleceń podaje się opcje dla awk, tekst programu (jeśli nie podano go poprzez opcję -f lub --file) i wartości, które mają być udostępnione w predefiniowanych zmiennych ARGC i ARGV.

Struktura programów AWK 
„AWK jest językiem służącym do przetwarzania plików tekstowych. Plik jest traktowany jako ciąg rekordów, przy czym domyślnie rekordem jest każdy wiersz. Każda linia jest podzielona na pola, więc można traktować pierwsze pole linii jako pierwsze słowo, drugie pole jako drugie słowo itd. Program AWK składa się z sekwencji instrukcji wzorzec-akcja. AWK czyta wejście linia po linii. Każda linia jest przeszukiwana pod kątem wzorców występujących w programie i dla każdego pasującego wzorca wykonywana jest akcja z nim skojarzona.” – Alfred V. Aho
Program AWK składa się z sekwencji instrukcji wzorzec-akcja oraz opcjonalnych definicji funkcji.
 wzorzec { instrukcje akcji }
 function nazwa(lista parametrów) { instrukcje }
gdzie wzorzec to zwykle jakieś wyrażenie, a akcja – lista komend. Wejście dzielone jest na rekordy, domyślnie oddzielone znakiem nowej linii.
Dla każdego rekordu wejścia awk dokonuje porównania, sprawdzając czy odpowiada on jakiemuś wzorcowi z programu AWK. Jeśli wzorzec będzie odpowiadał rekordowi, zostanie wykonana związana z nim akcja. Wzorce są sprawdzane w kolejności ich pojawienia się w programie. Domyślną akcją jest wypisanie rekordu.

Komendy i składnia 
Na komendy AWK składają się wywołania funkcji, nadawanie wartości zmiennym, obliczenia lub jakaś kombinacja wymienionych zadań. AWK posiada wbudowane wsparcie dla wielu funkcji. Niektóre wersje pozwalają na dynamiczne linkowanie bibliotek, co umożliwia korzystanie z jeszcze większej liczby funkcji.
Dla uproszczenia nawiasy klamrowe ({}) w poniższych przykładach zostaną pominięte.

print 
Polecenie print jest używane do wypisywania tekstu. Wyjście jest zawsze zakończone predefiniowanym separatorem rekordów (output record separator (ORS)), który domyślnie jest znakiem nowej linii. Najprostsze zastosowanie polecenia print to:

 print
 Wyświetla zawartość obecnego rekordu.
W AWK rekordy rozbijane są na pola, które można wyświetlić osobno:

 print $1
 Wyświetla pierwsze pole obecnego rekordu.

 print $1, $3
 Wyświetla pierwsze i trzecie pole obecnego rekordu oddzielone separatorem pola (output field separator (OFS)) – domyślnie spacją.

Choć zapis pól ($X) może kojarzyć się z zapisem określającym zmienne (jak np. w perlu), to jednak określa pola bieżącego rekordu. Ponadto $0 odnosi się do całego rekordu, więc "print" oraz "print $0" mają takie samo działanie.

print pozwala również wyświetlić wynik obliczeń lub wywołania funkcji. Np.
 print 3+2
 print foobar(3)
 print foobar(zmienna)
 print sin(3-2)
Wyjście można również przekierować do pliku:
 print "wyrażenie" > "nazwa pliku"
lub do innego polecenia przez użycie potoku:
 print "wyrażenie" | "polecenie"

Zmienne i tablice

Zmienne 
Zmienne AWK są dynamiczne — zaczynają istnieć, gdy są po raz pierwszy użyte. Nazwy zmiennych mogą zawierać znaki z zakresu [A-Za-z0-9_], lecz nie mogą być słowami kluczowymi. Zmienne oraz pola mogą być liczbami (zmiennoprzecinkowymi), łańcuchami lub jednym i drugim naraz. Interpretacja wartości zmiennej zależy od kontekstu. Jeśli jest użyta w wyrażeniu numerycznym, jest interpretowana jako liczba. Natomiast jeśli jest użyta w wyrażeniu łańcuchowym — jest traktowana jak łańcuch.

AWK posiada tablice jednowymiarowe. Symulowane mogą być również tablice wielowymiarowe. Podczas działania programu ustawianych jest kilka predefiniowanych zmiennych opisanych niżej.

Zmienne wbudowane 
Zmienne wbudowane w AWK to m.in. zmienne określające pola: $1, $2 itd. Zwracają one wartość lub tekst przechowywany aktualnie w określonym polu rekordu. Poza nimi AWK posiada również inne zmienne:

Tablice 
Tablice są indeksowane wyrażeniem ujętym w nawiasy kwadratowe ([]). Jeśli wyrażenie jest listą wyrażeń (wyrażenie, wyrażenie, ...), to indeks tablicy jest sklejany z wartości (łańcuchowych) każdego wyrażenia, oddzielonych wartością zmiennej SUBSEP. Jest tak dlatego, że AWK posiada wyłącznie tablice asocjacyjne, zaś klucze numeryczne pamiętane są jako łańcuchy.

Symulowanie tablic wielowymiarowych polega na sklejaniu poszczególnych indeksów w unikalny łańcuch. Na przykład:
i = "A" ; j = "B" ; k = "C"
x[i, j, k] = "hello, world\n"
przypisuje łańcuch "hello, world\n" elementowi tablicy x, o indeksie będącym łańcuchem "A\034B\034C".

Jeśli tablica posiada wielokrotne indeksy, można użyć konstrukcji (i, j) in array. Element można skasować z tablicy przy użyciu polecenia delete. Poleceniem delete można się też posłużyć do skasowania całej zawartości tablicy, przez podanie jej nazwy bez indeksu.

Funkcje 
Funkcje są wykonywane po wywołaniu ich z wyrażeń występujących we wzorcach lub akcjach. Definicja funkcji składa się ze słowa kluczowego function, jej nazwy, argumentów i ciała. Poniżej znajduje się przykład funkcji:
function dodaj_siedem (liczba) {
  return liczba + 7
}
Tę funkcję można wywołać w następujący sposób:
print dodaj_siedem(51)     # zwraca 58

Między nazwą funkcji, a nawiasem otwierającym można wstawić spację jedynie podczas jej deklaracji – w wywołaniu nawias musi stać bezpośrednio po nazwie funkcji. Ma to na celu zapobieżenie niejednoznaczności składni z operatorem konkatenacji  (łączenia). Ograniczenie to nie odnosi się do funkcji wbudowanych.

Funkcje mogą posiadać zmienne lokalne. Ich nazwy dodawane są na końcu listy argumentów w definicji funkcji. Jednak ich wartości powinno się pomijać wywołując daną funkcję. Zazwyczaj przed deklaracją zmiennych lokalnych dodaje się kilka białych znaków, by wskazać miejsce, w którym kończą się argumenty funkcji i zaczynają zmienne lokalne.

Zamiast słowa function można używać słowa funct.

Operatory 
Operatory w AWK, w kolejności malejącego priorytetu, to:

Wywołanie programu AWK 
Tekst programu czytany jest tak, jakby wszystkie pliki programu zostały połączone ze sobą w całość, przy czym pierwszeństwo mają pliki podane jako argumenty polecenia. Przydaje się to do budowania bibliotek funkcji AWK, bez konieczności włączania ich do każdego nowego programu AWK, który z nich korzysta. Umożliwia to również łączenie funkcji bibliotecznych z programami z wiersza poleceń. Zmienna środowiskowa AWKPATH określa ścieżkę przeszukiwania, używaną do znajdowania plików źródłowych podanych w opcji -f. Jeśli zmienna ta nie istnieje, domyślną ścieżką staje się .:/usr/local/share/awk. (Faktyczny katalog może być różny, zależnie od tego jak skompilowano i zainstalowano awk). Jeśli nazwa pliku, podana opcji -f zawiera znak /, nie jest dokonywane żadne przeszukiwanie ścieżki.

Program AWK wykonywany jest w następującej kolejności:
 inicjalizacja zmiennych podanych w opcjach -v,
 kompilacja do postaci wewnętrznej,
 wywołanie kodu zawartego w blokach BEGIN (o ile istnieją),
 odczytywanie plików podanych w tablicy ARGV,
 odczytywanie standardowego wejścia, jeśli pliki nie zostały podane.

Z poziomu wiersza poleceń można także ustawić wartość zmiennej, podając zamiast nazwy pliku ciąg zmienna=wartość. Ten sposób inicjalizowania zmiennych najbardziej przydaje się do dynamicznego nadawania wartości zmiennym, których AWK używa do określania sposobu, w jaki wejście rozbijane jest na pola i rekordy. Jest też użyteczny do kontroli stanu, jeśli zachodzi potrzeba wielokrotnego czytania danego pliku danych. Jeśli wartość konkretnego elementu ARGV jest pusta (""), to awk ją pomija.

Przykłady zastosowań

Hello World 
Poniżej znajduje się przykład programu "Hello world" napisanego w AWK:
BEGIN { print "Hello, world!" }
Niekoniecznie trzeba w tym wypadku pisać na końcu exit. Jedynym wzorcem jest BEGIN, więc żadne argumenty z linii komend nie są przetwarzane.
W niektórych wersjach AWK i niektórych krajach dodanie znaku ”_“  przed łańcuchem znaków wypisze go w języku narodowym. Np.
BEGIN { print _"Hello, world!" }
we Francji może wypisać bonjour, monde!.

Wypisywanie linii zawierających więcej niż 80 znaków 
length($0) > 80

Zliczanie liczby słów 
Program liczy liczbę słów na wejściu i wypisuje liczbę słów, linii i znaków podanych na wejściu (podobnie jak wc).
{
    w += NF
    c += length + 1
}
END { print NR, w, c }
Ze względu na brak wzorca w pierwszej linii programu akcja jest wykonywana dla każdego wiersza wejścia.

Suma numerów ostatnich słów 
{ s += $NF }
 END { print s + 0 }
s jest zwiększane o numer ostatniego słowa w każdym rekordzie.
Na końcu wejścia wzorzec END pasuje, więc s jest wypisywane. Jednak wejście może nie zawierać ani jednej linii. Wtedy, jako że zmiennej s nie została nadana żadna wartość, będzie to pusty ciąg. Dodanie zera na końcu przy wypisywaniu s wymusza traktowanie zmiennej tak, jakby zawierała ona liczbę. Dzięki temu nawet, gdy s jest pustym ciągiem na wyjściu nie będzie pustej linii, lecz 0.

Wypisanie określonej liczby pasujących linii wejścia 
 $ yes Wikipedia | awk 'NR % 4 == 1, NR % 4 == 3 { printf "%6d  %s\n", NR, $0 }' | sed 7q
      1  Wikipedia
      2  Wikipedia
      3  Wikipedia
      5  Wikipedia
      6  Wikipedia
      7  Wikipedia
      9  Wikipedia
 $

Tutaj komenda yes wypisuje słowo „Wikipedia” tak długo, aż sed wykryje, że zostało wypisanych 7 linii. Dalsza część polecenia wypisuje każdą z linii poprzedzoną jej numerem. Wypisywane są tylko linie, których numery dają resztę z dzielenia przez 4 o wartościach od 1 do 3.

Obliczanie częstotliwości występowania słów 
Program używa tablicy asocjacyjnej:
BEGIN {
    FS="[^a-zA-Z]+"
}
{
    for (i=1; i<=NF; i++)
        words[tolower($i)]++
}
END {
    for (i in words)
        print i, words[i]
}

Blok BEGIN ustawia separator pola na dowolny znak niebędący literą. Warto zauważyć, że separatory mogą być też wyrażeniami regularnymi.
Następnie następuje akcja wykonywana na każdej linii wejścia: dla każdego pola linii zwiększana jest liczba razy, jaką to słowo (uprzednio zamienione na małe litery) wystąpiło. Na końcu wypisywane są wszystkie słowa wraz z częstotliwościami występowania.

Samodzielne skrypty AWK 
Podobnie jak w wielu innych językach skrypt AWK można napisać poprzedzając go znakiem „shebang”. Dla przykładu komendę hello.awk wypisującą tekst „Hello, world!” można napisać tworząc plik o nazwie hello.awk posiadający następującą zawartość:
#! /usr/bin/awk -f
BEGIN { print "Hello, world!" }

Opcja -f informuje awk, że następny argument to nazwa pliku, z którego należy przeczytać program, a jest on tam umieszczany przez powłokę systemową podczas działania, tak jakby wywołane zostało polecenie /usr/bin/awk -f hello.awk.

Przypisy

Zobacz też 
 sed
 perl
 polecenia systemu operacyjnego Unix

Linki zewnętrzne 

 Strona domowa GNU Awk
 The Amazing Awk Assembler by Henry Spencer.
 
 Gawkinet: TCP/IP Internetworking with Gawk

Języki skryptowe