Linki i moje projekty

Programowanie w AWK

Tytułem wstępu zaznaczę że GAWK jest wersją interpretatora języka skryptowego AWK, opracowywaną w ramach projektu GNU, posiadającym własne rozszerzenia (zwane rozszerzeniami GNU). Z faktu iż jest to język skryptowy wynika iż program wykonywany jest tożsamy z kodem źródłowym - nie ma etapu kompilacji.

Składnia języka jest dość podobna do C, oczywiście nie znajdziemy tu zdecydowanej większości funkcji z biblioteki standardowej C oraz są też bardzo istotne różnice w samej budowie programu. Podstawową z tych różnic jest to iż program w języku AWK składa się z bloków "warunek { instrukcje }" oraz (opcjonalnie) definicji funkcji "function nazwa (argumenty) { instrukcje }". Taka budowa składni wynika z głównego zastosowania do jakiego został stworzony AWK - przetwarzania tekstu.

Skrypty AWK mogą być zapisywane go pliku - wywołujemy przez gawk -f sciezka_pliku, mogą też być podawane w linii poleceń: gawk 'tresc programu' (dla bezpieczeństwa treść tak podawanego programu należy ujmować w '' - inaczej powłoka może z nią zrobić coś niedobrego interpretując zawarte tam znaki specjalne ... . Możemy też w pliku z poleceniami AWK dodać jako pierwszą linię #!/usr/bin/gawk -f oraz dodać mu argument wykonywalności - będzie zachowywał się jak zwykły program przyjmujący dane z stdin lub plików podanych jako argumenty.

Tym razem zaczniemy tradycyjnie ... od "Witaj Świecie" (zakładam że plik w którym umieszczane są przykładowe programy nazywa się "hello.awk" i ma atrybut wykonywalności):

#!/usr/bin/gawk -f

{ printf "Hello World !!!\n" }

Wykonanie ./hello.awk spowoduje oczekiwanie na dane wejściowe z sdtin ... po wpisaniu każdej kolejnej linii program wypisze "Hello World !!!" (aby zkończyć Ctrl+D) ... natomiast ./hello.awk jakis_plik wypisze ileś razy ten napis i zakończy działanie ... ale chyba nie o to chodziło. Spróbujmy wiec tak:

#!/usr/bin/gawk -f

BEGIN { printf "Hello World !!!\n" }

Teraz ./hello.awk działa jak byśmy oczekiwali (identycznie też ./hello.awk jakis_plik) - wypisuje "Hello World !!!" i kończy.

Powyższe przykłady obrazują (chyba) podstawową własność języka AWK to co jest zawarte w klamrach wykonywane jest dla każdej pasującej linii (przed klamrami może stać warunek w stylu FNR == 1, co oznacza numer_rekordu == 1 bądź wyrażenie regularne np. ^T[ye] co oznacza że linia zaczyna się od T po którym jest y lub e) oraz są specjalne identyfikatory - poznany BEGIN i END wykonywane odpowiednio na początku i końcu programu. Widzimy też funkcję printf - bliźniaczo podobną do tej z C (powyżej pominąłem nawiasy ale jest to to samo co printf("Hello World !!!\n")).

Widzimy tez że nie ma średników, gdy instrukcja kończy się z końcem linii (ale nic nie stoi na przeszkodzie aby je stawiać), są natomiast konieczne gdy w jednej linii mamy kilka instrukcji.

Kolejnym ważnym aspektem są zmienne i tablice. Nie ma potrzeby określania typu zmiennej, wszystkie zmienne poza zmiennymi wewnętrznymi funkcji są globalne. AWK używa tablic asocjacyjnych - indeksem w tablicy może być cokolwiek (liczba lub napis), nie ma potrzeby ustalania długości tablicy ani typu elementów w niej przechowywanych; poniżej przykład ilustrujący tablice w AWK. Wszystkie te cechy ilustruje poniższy przykładowy kod

#!/usr/bin/gawk -f

function moja_funkcja( zmienna1 ) {
        zmienna2 = sin (2);
        zmienna1 = sprintf("%s @ %f", zmienna1, zmienna2)
        return zmienna1;
}

BEGIN {
        tablica["Hello"]="World";
        tablica["Witaj"]="Świecie";
        for (INDEX in tablica)
                printf("%s %s !!!\n", INDEX, tablica[INDEX])
}

FNR == 1 {
        zmienna3 = "Ala ma kota"
        moja_funkcja(zmienna3);
        # to jest komentarz
        # uwaga: miedzy nazwą funkcja a otwierającym nawiasem nie może być białych znaków
        printf("%s %s\n", zmienna3, zmienna1)
}
FNR == 2 {
        zmienna4 = 5
        zmienna4 = moja_funkcja(zmienna4);
        printf("%s %s\n", zmienna3, zmienna4)
}

Warto zaznaczyć że dość często wykorzystuje się tablice w których interesuje nas tylko index - jest tak dlatego że gwarantuje to unikalność (nie może być dwóch takich samych indeksów), zatem gdy zbieramy napisy i chcemy wyeliminować powtórzenia przyjemnym rozwiązaniem jest nasza_tablica[napis]=0. Oczywiście mogą być też tablice dwu lub więcej wymiarowe: nasza_tablica[indeks1,index2]=wartosc. Na koniec warto zaznaczyć że oprócz powyższego wywołania for mamy też zupełnie standardowe pętle for, while znane z C.

Innym częstym zagadnieniem jest wypisanie wszystkich pól od wskazanego do końca rekordu. Możemy do tego celu użyć funkcji uzytkownika jak w poniższym przykładzie:

function to_end(i,  tmp) {tmp=$i; for (++i; i<=NF; i++) tmp = tmp OFS $i; return tmp;}
{
	print to_end(3)
}

Więcej (w szczególności jak budować wyrażenia regularne oraz wykaz predefiniowanych funkcji) - patrz man 1 gawk oraz:

Skrypty AWK'owe często wykorzystywane są w skryptach powłoki (bash, ...), nie stoi też nic na przeszkodzie aby wykorzystywać je w C - np. przez system() lub exec(); . Warto tutaj zwrócić uwage na przekazywanie zmiennych bashowych do awk - mozna to zrobić (w przypadku prostych skryptów poprzez umieszczenie skryptu w podwójnych cudzysłowiach i zabezpieczniu tych znaków specjalnych których bash nie powinien dotykać poprze \ - np. awk "(\$1==$bash) {print \$0}" (zmienną bashową którą przekazujemy awk jest $bash) albo poprzez przekazanie zmiennej opcją -v - np. awk -v temp=$temp '($1==temp) {print $0}'.

AWK jest także przydatnym narzędziem do wykonywania obliczeń (obliczania średnich, odchyleń, różnic, etc) na danych będących wynikami jakiś symulacji lub pomiarów.

Linki i moje projekty

Zamieszczam tutaj krótki skrypcik który usuwa zadany katalog ze zmiennej systemowej PATH - PATH=`echo $PATH | awk 'BEGIN {RS=":"; FS="\n"} $1!="/sciezka/katalogu/do/usuniecia" {printf("%s:",$1)}'`, a także przykłady skryptów bashowych wykorzystujących AWK:

  • beos_mail2mbox.sh - konwersja skrzynki pocztowej z BeOSa do standardowego mbox'u
  • net_stat.sh - statystyki transferu na zadanym interfejsie sieciowym
  • smtp.sh - prosty serwer SMTP (wykorzystujący do nasłuchu netcat'a), umożliwia sterowanie (wykonywanie zdalnych poleceń przy pomocy odpowiednio spreparowanych maili (jest wymagającym w teorii tylko busybox'a zastępnikiem jakiegoś serwera, pocztowego który otrzymane maile przekazuje do skryptu wyciągającego z nich potrzebne dane przy pomocy grep/cut/awk)
  • engine-parse_menu.awk, engine-generate.sh - dość rozbudowany skrypt bash'owo-awk'owy obsługującego moją stronę www (więcej strona projektu)


Copyright (c) 1999-2015, Robert Paciorek (http://www.opcode.eu.org/), BSD/MIT-type license


Redystrybucja wersji źródłowych i wynikowych, po lub bez dokonywania modyfikacji JEST DOZWOLONA, pod warunkiem zachowania niniejszej informacji o prawach autorskich. Autor NIE ponosi JAKIEJKOLWIEK odpowiedzialności za skutki użytkowania tego dokumentu/programu oraz za wykorzystanie zawartych tu informacji.

This text/program is free document/software. Redistribution and use in source and binary forms, with or without modification, ARE PERMITTED provided save this copyright notice. This document/program is distributed WITHOUT any warranty, use at YOUR own risk.

Valid XHTML 1.1 Dokument ten (URL: http://www.opcode.eu.org/programing/awk) należy do serwisu OpCode. Autorem tej strony jest Robert Paciorek, wszelkie uwagi proszę kierować na adres e-mail serwisu: webmaster@opcode.eu.org.
Data ostatniej modyfikacji artykulu: '2015-10-10 11:34:53 (UTC)' (data ta może być zafałszowana niemerytorycznymi modyfikacjami artykułu).