AWK

język programowania

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
logo
Pojawienie się

1977

Paradygmat

skryptowy, proceduralny, sterowany zdarzeniami

Typowanie

brak; ciągi znaków, liczby całkowite i zmiennoprzecinkowe; wyrażenia regularne

Implementacje

awk, GNU Awk, mawk, nawk, MKS AWK, Thompson AWK (compiler), Awka (compiler)

Aktualna wersja stabilna

IEEE Std 1003.1-2004 (POSIX) / 1985

Twórca

Alfred Aho, Peter Weinberger, Brian Kernighan

Platforma systemowa

wieloplatformowy

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 edytuj

„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[1]

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 edytuj

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 edytuj

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 edytuj

Zmienne edytuj

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 edytuj

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:

Zmienna Zastosowanie
FILENAME Zawiera nazwę pliku wejściowego.
FS Przechowuje separator pól rekordu (field separator). Domyślnie jest to biały znak, czyli spacja lub znak tabulacji. FS można przypisać inną wartość określającą nowy separator.
RS Przechowuje separator rekordów (record separator). Jako że domyślnie każda linia jest nowym rekordem, domyślna wartość RS to znak nowej linii.
NF Liczba pól w bieżącym rekordzie wejściowym. Przy pomocy tej zmiennej można odwołać się do ostatniego pola rekordu. W tym celu wystarczy wpisać $NF
NR Całkowita liczba odczytanych do tej pory rekordów wejściowych.
OFS Separator pól wyjściowych (output field separator). Domyślnie jest to spacja.
ORS Separator rekordów wyjściowych (output record separator), oddzielający drukowane rekordy. Domyślnie jest to nowa linia.
OFMT Przechowuje format wyjściowy liczb, domyślnie "%.6g".
SUBSEP Znak, używany do separacji wielokrotnych indeksów w elementach tablicowych, domyślnie jest to "\034".

Tablice edytuj

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 edytuj

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 edytuj

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

Operator Znaczenie
(...) Grupowanie
$ Odniesienie się do pola
++ -- Inkrementuj i dekrementuj, zarówno przedrostkowe i przyrostkowe
^ Potęgowanie (można użyć również **, oraz **= dla operatora przypisania)
+ - ! Jednoargumentowy plus, minus i logiczna negacja
* / % Mnożenie, dzielenie i modulo
+ - Dodawanie i odejmowanie
' ' (odstęp) Konkatenacja (złączenie) łańcuchów
< > <= >= != == Regularne operatory relacyjne
~ !~ Dopasowanie wyrażenia regularnego, dopasowanie zanegowane
in Przynależność do tablicy
&& Koniunkcja logiczna AND
|| Alternatywa logiczna OR
?: Wyrażenie warunkowe z C. Ma ono postać wyraż1 ? wyraż2 : wyraż3. Jeśli wyraż1 jest prawdziwe, to wartością wyrażenia jest wyraż2, w przeciwnym wypadku jest nią wyraż3. Obliczane jest wyłącznie jedno z wyraż2 i wyraż3
= += -= *= /= %= ^= Przypisanie. Obsługiwane jest zarówno przypisanie absolutne (zmienna = wartość) jak i przypisanie operatorowe (inne formy)

Wywołanie programu AWK edytuj

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:

  1. inicjalizacja zmiennych podanych w opcjach -v,
  2. kompilacja do postaci wewnętrznej,
  3. wywołanie kodu zawartego w blokach BEGIN (o ile istnieją),
  4. odczytywanie plików podanych w tablicy ARGV,
  5. 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ń edytuj

Hello World edytuj

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 edytuj

length($0) > 80

Zliczanie liczby słów edytuj

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 edytuj

{ 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 edytuj

$ 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 edytuj

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 edytuj

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 edytuj

Zobacz też edytuj

Linki zewnętrzne edytuj