Język przetwarzania tekstów AWK AWK jest językiem interpretowanym (niekompilowanym) i pozwala na prostą manipulację strukturami danych takimi jak dane tekstowe. Jest używany do takich zadań jak: tworzenia raportów, dodawania dodatkowych funkcji do edytorów tekstu takich jak vi, konwersji z jednego formatu pliku na inny, tworzenia małych baz danych, prowadzenia obliczeń matematycznych z użyciem plików tekstowych. Zasada działania języka opiera się na zasadzie: wczytywany jest pojedynczy rekord, i dla niego wykonywane są wszystkie instrukcje programu. Potem czytany jest następny, i znowu wykonywane są dla niego przeznaczone instrukcje. Programy pisane w awk różnią się od programów napisanych w większości innych języków, gdyż są sterowane danymi (datadriven): to znaczy, opisujemy dane, z którymi chcemy pracować, a następnie co zrobić po ich znalezieniu. Inne języki przeważnie są proceduralne; musimy opisać, bardzo szczegółowo, każdy krok jaki ma być wykonany przez program. Pracując w językach proceduralnych zwykle dużo trudniej jest precyzyjnie opisać dane, jakie będzie przetwarzał program. Z tego powodu, programy awk są często łatwe zarówno w pisaniu jak i czytaniu. Składnia wywołania programu jest przedstawiona poniżej: $ awk 'program' plik_wejsciowy1 plik_wejsciowy2... gdzie sekcja program zawiera reguły, natomiast pliki wejściowe są plikami z danymi. Przykładem wykonania takiego polecenia jest np. $ awk 'length($0) > 80' data Jeżeli chcemy wykonywać większe programy z większą ilością reguł (zapisanych w oddzielnym pliku) możemy użyć następującej składni polecenia: $ awk f plik_z_programem plik_wejsciowy1 plik_wejsciowy2... Można tworzyć wykonywalne programy z użyciem awk-a wstawiając do wykonywalnego pliku odpowiednią dyrektywę, a następnie pisząc instrukcje awk-a: #! /bin/awk f BEGIN { print "AWK Tekst" } Możemy tworzyć komentarze wstawiając znak # w danej linii przed tekstem komentarza. Instytut Informatyki AP 2010 (modified: 2010-03-13 12:30) strona 1
Działanie programu awk przedstawimy na poniższym przykładzie: Stwórzmy plik z danymi o poniższej strukturze i zapiszmy jako 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 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 Napiszmy prosty program: $ awk '/foo/ { print $0 }' BBS-list (wyświetl linie zawierające ciąg znakowy foo) Teraz nieco trudniejszy program z dwiema regułami: $ awk '/12/ { print $0 } \ > /21/ { print $0 }' BBS-list (wyświetl linie zawierające ciągi znakowe 12 i 21) Dodatkowo istnieją dwa warunki specjalne: BEGIN i END. Pierwszy jest prawdziwy na samym początku, zanim jeszcze zostanie wczytany pierwszy rekord z jakiegokolwiek pliku. Drugi jest prawdziwy po zakończeniu czytania wszystkich plików. Można pominąć w wyrażeniu albo warunek (wtedy blok będzie wykonywany dla każdego rekordu}, albo część z poleceniami (łącznie ze znakami { i }) - wtedy domyślnym blokiem instrukcji jest wypisanie rekordu na standardowe wyjście. Wywołanie programu awk z użyciem potoków: $ ls -lg awk '$5 == "lis" { sum += $4 } END { print sum }' Polecenie to spowodowało wyświetlenie łącznej objętości plików utworzonych w listopadzie. Można używać instrukcji warunkowych (wcześniej musimy utworzyć plik data): $ awk '{ if (length($0) > max) max = length($0) } \ > END { print max }' data (wyświetla długość najdłuższej linii z pliku data) Instytut Informatyki AP 2010 (modified: 2010-03-13 12:30) strona 2
Jak również pętli: $ awk 'BEGIN { for (i = 1; i <= 7; i++) \ print int(101 * rand()) }' (wyświetla siedem losowych liczb z zakresu od 0 do 100) Można rozszerzać polecenia używając programu awk: $ awk -F: '{ print $1 }' /etc/passwd sort (wyświetla posortowaną listę loginów użytkowników) Można wyświetlić wybrane linie tekstu: $ awk 'NR % 2 == 0' data (wyświetla parzyste linie tekstu) Inne przykłady użycia awk: $ awk '{ $3 = $2-10; print $2, $3 }' (użycie wyrażeń matematycznych) W awk nie ma deklaracji zmiennych. Wszystkie zmienne są inicjalizowane w momencie użycia, a przy inicjalizacji otrzymują wartość 0 lub "" (pusty napis), w zależności od kontekstu w jakim zmienna została użyta. Wszystkie tablice zmiennych w awk są asocjacyjne, tzn. są indeksowane nie liczbami, a napisami. Można się też odwoływać do tablic przez indeksy: wtedy liczba jest zamieniana na napis, i dalej następuje odwołanie do elementu indeksowanego napisem. Operatory są podobne do tych z języka C i tak np. przypisanie to będzie znak =, a porównanie ==. Przykład: $ awk 'BEGIN { for (i = 1; i <= 10; i++) \ > { tab[i]=i; \ > if(i%2) \ > print tab[i]; }}' (wyświetli wszystkie nieparzyste liczby z zakresu od 0 do 10) Awk udostępnia liczne zmienne specjalne, które są ustawiane przez sam interpreter, lub mają bezpośredni wpływ na działanie programu. Podstawowe takie zmienne to zmienne (używane w poprzednich przykładach) poprzedzone znakiem dolara: $0, $1, $2,... Po wczytaniu dowolnego rekordu jest on zapamiętywany w zmiennej $0. Dodatkowo jest dzielony na pola, oddzielone od siebie separatorami. Przykładowo jeśli mamy linię z napisem "aaa bbb ccc", a separatorem jest spacja, to pola mają kolejno wartości: "aaa", "bbb", "ccc". I takie wartości też są przypisywane kolejno zmiennym: $1, $2, $3. Innym rodzajem zmiennych są zmienne takie jak: NF (liczba wczytanych pól), FS (separator pól - tu wpisywane jest pełne wyrażenie regularne, które opisuje separatory (np. "a b"). $ awk 'NF > 0' data (wyświetli wszystkie linie z wyjątkiem tych, które są puste) Instytut Informatyki AP 2010 (modified: 2010-03-13 12:30) strona 3
Wyjątkiem jest separator " " (spacja), który pasuje do dowolnej liczby spacji, tabulatorów, itp. Początkowe i końcowe separatory (np. jak w " abc abc ") są ignorowane. Jeśli FS jest puste, to rekordy są dzielone na pojedyncze znaki. Oprócz separatora pól mamy też separator rekordów, trzymany w zmiennej RS. Domyślnie separatorem jest znak nowej linii (\n) Przy wypisywaniu rekordów na wyjście możemy posługiwać się innymi separatorami niż te, które były używane przy wejściu: analogicznie do FS i RS dostępne są też OFS i ORS (output field/record separator) Kolejne to: NR (numer wczytanego rekordu) i FNR (numer wczytanego rekordu z aktualnego pliku). Nazwa aktualnie przetwarzanego pliku jest trzymana w zmiennej FILENAME. Następne zmienne: ARGC i ARGV, pierwsza z nich to ilość parametrów podanych do programu, druga to tablica tych parametrów, indeksowana od 0. ARGV[0] to nazwa uruchomionego interpretera (na ogół "awk"), pozostałe to rzeczywiste parametry, odpowiadające nazwom plików z których AWK bierze kolejne rekordy. Inne zmienne: zmienna ENVIRON. To jest tablica, która trzyma wszystkie zmienne środowiskowe. $ echo a b c d awk '{ OFS = ":"; $2 = "" > print $0; print NF }' (powoduje usunięcie drugiego pola i nadanie znaków separacji :, a następnie wyświetlenie całego rekordu i liczby wczytanych rekordów) $ echo a b c d awk '{ OFS = ":"; $2 = ""; $6 = "new" > print $0; print NF }' $ awk BEGIN {print ENVIRON[ HOME ]} (wypisuje zawartość zmiennej środowiskowej $HOME) Instytut Informatyki AP 2010 (modified: 2010-03-13 12:30) strona 4
Użycie wyrażeń regularnych Wyrażenia regularne mogą być używane jako wzorce zamknięte w znaki ukośnika /. Można ich używać również w instrukcjach warunkowych oraz pętlach. Wyrażenie ~ /wyrreg/ - jest prawdziwe, gdy ciąg znakowy pasuje do ciągu z pomiędzy znaków ukośnika np. $ awk {if ($0 ~ /peter/ $0 ~ /paul/) \ > print "znaleziono"; \ > else "nie znaleziono";} imiona Wyrażenie!~ /wyrreg/ - jest prawdziwe, gdy ciąg znakowy nie pasuje do ciągu z pomiędzy znaków ukośnika Znaki kontrolne rozpoczynające się od znaku \. Poniżej przykłady takich znaków. \xnn znak o kodzie ASCII nn \x{nnnn} znak o kodzie nnnn (jeden bajt dla tekstu ASCII, dwa bajty dla znaków Unicode) \t znak TAB (HT/TAB), to samo co \x09 \n nowa linia (NL), to samo co \x0a \r powrót karetki (CR), to samo co \x0d \f form feed (FF), to samo co \x0c \a alarm (BEL), to samo co \x07 \e escape (ESC), to samo co \x1b Przykład: $ awk 'BEGIN { print "He said \"hi!\" to her." }' Operatory wyrażeń regularnych (metaznaki). Metaznak ^ - dopasowuje początek ciągu znakowego np. if ("line1\nline 2" ~ /^L/) Metaznak $ - dopasowuje koniec ciągu znakowego np. if ("line1\nline 2" ~ /1$/) Metaznak. dopasowuje pojedynczy znak Instytut Informatyki AP 2010 (modified: 2010-03-13 12:30) strona 5
Listy znakowe ujęte w nawiasy kwadratowe [] dopasowują do któregokolwiek znaku z listy np. [MXK] dopasuje jeden ze znaków umieszczonych w nawiasach, [1-9] dopasuje cyfry od 1 do 9 [^0-9] dopasuje dowolny znak z wyjątkiem cyfr Metaznak - jest operatorem alternatywy np. ^P [0-9] Metaznak iterator * dopasowuje dowolny ciąg znakowy składający się z 0 lub większej ilości znaków $ awk '/\(c[ad][ad]*r x\)/ { print }' sample dopasuje i wyświetli linie pasujące do wzorców jak (car x), (cdr x), itp.) Pozostałe metaznaki iteratory: * zero lub więcej, podobne do {0,} (greedy) + jeden lub więcej, podobne do {1,} (greedy)? zero lub jeden, podobne do {0,1} (greedy) {n} dokładnie n razy (greedy) {n,} co najmniej n razy (greedy) {n,m} przynajmniej n razy, ale nie więcej niż m razy (greedy) *? zero lub więcej, podobne do {0,}? (nie greedy) +? jeden lub więcej, podobne do {1,}? (nie greedy)?? zero lub jeden, podobne do {0,1}? (nie greedy) {n}? dokładnie n razy (nie greedy) {n,}? przynajmniej n razy (nie greedy) {n,m}? co najmniej n razy, ale nie więcej niż m razy (nie greedy) Przykłady: wh{3}y dopasuje `whhhy' ale nie `why' czy `whhhhy'. wh{3,5}y dopasuje `whhhy' lub `whhhhy' lub `whhhhhy' - tylko takie. wh{2,}y dopasuje `whhy' lub `whhhy', i tak dalej. Instytut Informatyki AP 2010 (modified: 2010-03-13 12:30) strona 6
Ćwiczenie Utworzyć plik coins.txt o następującej zawartości (tylko to co w ramce): metal weight in ounces date minted country of origin description gold 1 1986 USA American Eagle gold 1 1908 Austria-Hungary Franz Josef 100 Korona silver 10 1981 USA ingot gold 1 1984 Switzerland ingot gold 1 1979 RSA Krugerrand gold 0.5 1981 RSA Krugerrand gold 0.1 1986 PRC Panda silver 1 1986 USA Liberty dollar gold 0.25 1986 USA Liberty 5-dollar piece silver 0.5 1986 USA Liberty 50-cent piece silver 1 1987 USA Constitution dollar gold 0.25 1987 USA Constitution 5-dollar piece gold 1 1988 Canada Maple Leaf Wyświetlić wszystkie linie zawierające informacje o monetach wykonanych ze złota. Wyświetlić opis (description) monet wykonanych ze złota. Wyświetlić wszystkie monety wybite przed 1980 rokiem Policzyć ile monet znajduje się w kolekcji Policzyć jaką wartość mają złote monety, przyjmując że mają one wartość 425$ Napisać program obliczający wg poniższego wzoru: Podsumowanie mojej kolekcji: Zlotych monet: Waga zotych monet: Wartość złotych monet: Srebrnych monet: Waga srebrnych monet: Wartość srebrnych monet: Wszystkich monet: Wartość kolecji: nn nn.nn n,nnn.nn nn nn.nn n,nnn.nn nn n,nnn.nn Instytut Informatyki AP 2010 (modified: 2010-03-13 12:30) strona 7