2. 1996 GUST, Zeszyt 7 11
> awk -f test1.awk plik.we przykład 1 (Nelson H. F. Beebe)
Co się stanie? AWK sprawdzi czy w pliku wej- Poniższy pomysłowy program przepisuje plik wejściowy
zastępując kolejne puste linie, jedną pustą linią.
ściowym plik.we istnieją linie zawierające napis
(i tylko napis) TeX, a następnie wypisze je do NF == 0 { nb++ }
standardowego wyjścia, czyli na ekran monitora.
NF > 0 { if (nb > 0) print "";
Taki będzie efekt, spójrzmy teraz nieco bliżej
nb = 0; print $0; }
na powyższy przykład. Najpierw sprawy oczywi-
ste: $0=="TeX" jest wzorcem, zaś { print $0 }
jest akcją. Akcja wykona się tylko wtedy gdy waru-
awk, gawk, nawk...
nek $0=="TeX" jest prawdziwy, czyli gdy linijka
w pliku wejściowym symbolizowana przez $0 jest Istnieje wiele interpreterów AWK-a; w systemach
napisem TeX. Instrukcja print $0 oznacza, że UNIX-o podobnych, dostarczane razem z syste-
AWK wypisze żądaną linijkę. mem, są one dziełem jego dostawcy. Istnieje też
Program w języku AWK może zawierać wiele kilka wersji ogólnodostępnych, takich jak: GAWK
par „ wzorzec { akcja }”. Dla każdej linii pliku – firmowany przez Free Software Foundation czy
wejściowego obliczane są kolejne wzorce (w kolej- mawk Michaela Brennana.
ności ich występowania w programie) i wykony- Różne implementacje AWK-a nie są w 100%
wane akcje. Przykład 1 ilustruje program AWK-owy kompatybilne ze sobą. Ponadto wiele implementa-
wykorzystujący 2 wzorce 3 . cji oferuje rozszerzenia w stosunku do standardu
Zanim przejdziemy do bardziej szczegóło- (za standard przyjmujemy opis z [1]). W niniej-
wych informacji o języku wymienimy kilka pod- szym artykule przedstawimy standard AWK-a oraz
stawowych reguł składni AWK-a. rozszerzenia oferowane przez interpreter GAWK
Kolejne pary „ wzorzec { akcja }” muszą w wersji 3.0. Gorąco polecamy wszystkim tę imple-
być oddzielone średnikami lub znakami nowej mentację AWK-a, którą sami używamy od kilku lat.
linii. Implementacja GNU interpetera AWK, po-
Akcje mogą składać się z wielu poleceń, wstała w 1986 r. dzięki pracy Paula Rubina
które muszą być oddzielone średnikami lub i Jay Fenlason we współpracy z Richardem Stall-
znakami nowej linii. manem i Johnem Woodsem. Została ona gruntow-
Wzorzec lub akcja może zostać pominięty. nie zmieniona w 1989 r. przez Davida Truemana
W wypadku braku wzorca akcja zostaje wy- i Arnolda Robbinsa. W styczniu 1996 r. ukazała się
konana dla każdej linii pliku wejściowego. Je- wersja 3.0 interpretera GAWK, zawierająca kilka in-
żeli pominiemy akcję, to AWK zastosuje akcję teresujących rozszerzeń w stosunku do standardu.
domyślną, czyli { print $0 }. W dystrybucji znajduje się także ponad 300 stroni-
W powyższym przykładzie AWK czyta dane cowy podręcznik ([4]), zawierający kompletny opis
z jednego pliku wejściowego (plik.we) ale w ogól- języka i wiele ciekawych przykładów.
ności możemy uruchomić AWK-a w następujący Uwagi: Opis rozszerzeń jest specjalnie oznaczony
sposób: w tekście za pomocą pisma pochyłego.
PRZEDSTAWIONE PRZYKŁADY SĄ PRZY-
awk -f program plik1 plik2 ...
GOTOWANE DO WYKONYWANIA W SYSTEMIE
W takiej sytuacji AWK czyta po kolei linie z pliku1 ,
DOS. Użytkownicy UNIX-a, których zainteresuje
pliku2 itd. dla wszystkich plików których nazwy
nasz artykuł, nie powinni mieć większych pro-
podano w linii komend. Pliki te są modyfikowane
blemów ze zmianą tych fragmentów programów,
wg programu z pliku program .
które są „DOS-owo zorientowane”.
3: Programy AWK-owe prezentowane we fragmen-
Struktura pliku wejściowego
tach tekstu rozpoczynących się słowem przykład,
często zawierają konstrukcje języka jeszcze nie omó- Dla AWK-a dane wejściowe składają się z rekordów,
wione. Jeżeli coś jest niezrozumiałe, czytaj dalej, które rozdzielone są separatorami RS. Standar-
a po lekturze całego artykułu wróć do do tego dowo rekordem jest cała linia, czyli separatorem
miejsca – wszystko powinno być jasne. jest znak końca linii.
3. 12 GUST, Zeszyt 7 1996
Rekordy podzielone są na pola, które roz- Wzorce BEGIN i END nie mogą być częścią wzorca
dzielone są separatorami pól FS. Standardowo złożonego. Podobnie częścią wzorca złożonego nie może
separatorem pól jest znak spacji lub tabulacji. być wzorzec z przecinkiem.
RS i FS są zmiennymi, a więc można im
nadać wartość. Przykładowo, jeśli zmiennej FS BEGIN/END. Wzorzec BEGIN nie pasuje do żad-
nadamy wartość ‘;’, to spacje i tabulatory nie będą nej linii z pliku wejściowego, a odpowiadająca mu
separatorami pól lecz znaki ‘;’. akcja jest wykonywana przed przeczytaniem przez
W akcjach i wzorcach, do wartości pól można AWK-a pierwszego znaku z tego pliku. Podobnie
odwoływać się zmiennymi postaci $ nr-pola . Tak instrukcje wzorca END wykonywane są po prze-
więc $1 to pierwsze pole rekordu, $2 drugie itd. czytaniu wszystkich znaków pliku wejściowego.
$0 oznacza cały rekord. Wbudowana zmienna NF Możliwe jest umieszczenie wielu wzorców BEGIN
przechowywuje liczbę pól bieżącego rekordu. i END w programie AWK-owym; są one wtedy wy-
konywane po kolei. Z reguły użytkownicy AWK-a
umieszczają wzorce BEGIN na początku a END na
Wzorce
końcu pliku. Dla AWK-a jest to absolutnie obojętne.
Wzorce służą do wyznaczenia tych linii tekstu, Jednym z najczęstszych sposobów użycia BE-
dla których wykonane mają być odpowiednie GIN jest zmiana domyślnego sposobu w jaki AWK
akcje. W ogólnym wypadku wzorzec może być dzieli linie czytanego pliku na pola. Wbudowana
kombinacją wyrażeń logicznych i wyrażeń regu- zmienna FS definiuje jaki znak jest separatorem
larnych. Ponieważ wzorce są wyrażeniami logicz- pól w rekordzie. Domyślnie pola oddzielone są
nymi, dozwolone są operatory logiczne: &&, ||, znakami spacji lub/i tabulacji. Taki sposób dzielenia
! oraz nawiasy. Istnieją dwa specjalne wzorce rekordu odpowiada sytuacji kiedy FS jest nadana
o nazwie BEGIN i END. Oto ogólna specyfikacja wartość równa spacji (FS=" "). Przy uruchomieniu
wzorców: programu zawierającego tylko wzorce BEGIN AWK
nie oczekuje w linii komend nazwy żadnego pliku
Wzorce wejściowego, por. przykład 10, s. 21.
BEGIN{ akcja } Wyrażenie. Wzorcami mogą być wyrażenia aryt-
akcja jest wykonywana przed otwarciem pliku wejścio- metyczne lub napisowe. Odpowiednia akcja
wego. jest wykonywana za każdym razem gdy takie
END{ akcja }
wyrażenie ma wartość różną od zera lub od na-
pisu pustego. Przykłady: $3/$2 >= 10, NR < 9 czy
akcja jest wykonywana po zamknięciu pliku wejścio-
wego. "NY3".
wyrażenie { akcja } Wzorzec regularny. Wzorzec regularny to wyraże-
akcja jest wykonywana za każdym razem gdy wartość nie regularne ujęte w parę znaków /. Podstawowe
wyrażenia jest równa prawda tj. jest niezerowe (dla sposoby użycia wzorca regularnego to:
wyrażeń numerycznych) lub niepuste (dla napisów).
/r/
/ wyrażenie-regularne /{ akcja } Pasuje do bieżącej linii pliku wejściowego jeżeli
akcja jest wykonywana za każdym razem gdy linia zawiera ona podnapis pasujący do wyrażenia
pliku wejściowego zawiera ciąg znaków pasujący do regularnego r.
wyrażenia-regularnego .
wyrażenie ~ /r/
wzorzec-złożony { akcja }
Pasuje do napisu będącego wartością wyrażenia
akcja jest wykonywany za każdym razem gdy linia czy- jeżeli zawiera on podnapis pasujący do wyrażenia
tanego przez AWK pliku zawiera ciąg znaków pasujący
do wzorca złożonego .
regularnego r. Zapis /r/ jest równoważny formie
$0 ~ /r/.
wzorzec1 , wzorzec2 { akcja }
wyrażenie !~ /r/
akcja jest wykonywana dla wszystkich linii od linii
zawierającej wzorzec1 do linii zawierającej wzorzec2 Pasuje do napisu będącego wartością wyrażenia
(łącznie z tymi liniami). Wzorzec oznacza wyrażenie jeżeli nie zawiera on podnapisu pasującego do
bądź wyrażenie-regularne . wyrażenia regularnego r.
4. 1996 GUST, Zeszyt 7 13
Wzorzec złożony. Wzorzec złożony to wyraże-
nie złożone z wzorców i operatorów logicznych c znak nie będący metaznakiem
c znak sterujący albo znak/metaznak c
||, &&, !. Wzorzec złożony pasuje do bieżącej . dowolny znak
linii pliku wejściowego jeżeli wartością wyraże- ^ początek napisu
nia jest prawda (czyli jest niezerowa lub niepusta). $ koniec napisu
Przykład: [ab...] dowolny ze znaków a, b...
[^ab...] dowolny ze znaków oprócz a, b...
$2 > 0.50 && $5 > 0.95 [a-z] dowolny ze znaków z zakresu a-z
[^a-z] dowolny ze znaków oprócz a-z
Wzorzec z przecinkiem. Pasuje do wszystkich li-
r1 |r2 r1 lub r2 (r oznacza wyrażenie regularne)
nii, od linii pasującej do wzorca1 do linii pasującej r* zero lub więcej napisów pasujących do r
do wzorca2 (łącznie z tymi liniami). Przykład: r+ jeden lub więcej napisów pasujących do r
/StartCharMetrics/,/EndCharMetrics/ r? zero lub jeden napis pasujący do r
(r) r (nawiasy służą do grupowania wyrażeń)
Wyrażenia regularne
Grupę znaków ujętą w nawisy klamrowe na-
Używając AWK-a nie sposób pominąć wyrażeń re- zywamy listą. Do takiego wyrażenia regularnego
gularnych, które stanowią o sile tego języka. Nie pasuje jeden dowolny znak z listy. Zakres znaków
będziemy przytaczać ścisłej matematycznej defini- to dwa znaki ujęte w nawiasy klamrowe oddzie-
cji, gdyż zaciemnilibyśmy tylko bardzo intuicyjne lone znakiem - (minus). Zakresy interpretowane
i łatwe naszym zdaniem pojęcie. są według kolejności wartości kodów ASCII ja-
Siła wyrażeń regularnych polega na możli- kie posiadają poszczególne znaki. Zatem specy-
wości stosowania uniwersalnych wzorców, które fikacja [0-9] jest równoważna [0123456789],
pasują (opisują) pewien zbiór napisów. Na pewno zaś [A-Da-d] liście [ABCDabcd].
każdy z nas wydał polecenie swojemu systemowi Dopełnieniem listy/zakresu jest lista/zakres
operacyjnemu, w którym zawarty był znak *; na z poprzedzającym znakiem ^ (bezpośrednio po
przykład: otwierającym nawiasie [). Przykładowo specyfi-
> emacs *.tex kacja [^0-9] oznacza jeden dowolny znak ale
Jeśli nie zdarzyło Ci się wydać takiego polecenia, nie cyfrę; [^A-ZĄĆĘŁŃÓŚŹ ] dowolny znak nie
to już wyjaśniamy! emacs to popularny edytor będący dużą literą alfabetu.
tekstowy, zaś napis *.tex oznacza, że chcemy Wewnątrz listy/zakresu wszystkie znaki oprócz
edytować wszystkie pliki z rozszerzeniem .tex 4 , ^, - tracą swoje metaznaczenie. Przykładowo:
z bieżącego katalogu. Znak * jest właśnie wyraże- [...] oznacza trzy kropki (a nie trzy dowolne
niem regularnym, które oznacza w tym wypadku znaki) zaś ^[^^] wszystkie znaki oprócz znaku ^
dowolną (z dokładnością do ograniczeń naszego na początku napisu.
systemu operacyjnego) ilość i rodzaj znaków. Nawiasy okrągłe służą do grupowania. Przy-
Wyrażenia regularne to wyrażenia umożliwia- kładowo: /(X|XX)(I|II|III)/ pasuje do na-
jące specyfikowanie klas napisów. O napisie nale- stępujących liczb XI, XII, XIII, XXI, XXII, XXIII.
żącym do tej klasy mówimy, że pasuje do wyraże- Znaki sterujące, zapisujemy w konwencji ję-
nia regularnego. Wyrażenia regularne są konstru- zyka C. Są to: a (dzwonek, alarm), b (znak
owane z następujących elementów: „normalnych cofnięcia, backspace), f (znak końca strony, form
znaków” (wszystkie litery, cyfry, większość pozo- feed), n (przejście do nowego wiersza, new line),
stałych znaków) oraz metaznaków , ^, $, ., [, r (carriage return), t (znak tabulacji). Ponadto
], |, (, ), *, +, ?. Poniższa tabela przedstawia znak oznacza , zaś każdy znak możemy za-
poszczególne elementy wyrażeń regularnych. pisać przy pomocy kodu ósemkowego używając
konwencji cyfra cyfra cyfra .
Wyrażenia regularne
Wyrażenie Znaczenie przykład 2 (wyrażenia regularne)
/^[ t]*$/ pasuje do napisu składającego się tylko
ze znaków spacji, tabulacji i napisu pustego;
4: Czyli takie pliki, których nazwa kończy się /^[^ t]*$/ pasuje do wszystkich napisów oprócz
znakami .tex. składających się ze spacji, znaków tabulacji i pustych;
5. 14 GUST, Zeszyt 7 1996
/[+-]?[0-9]+[.]?[0-9]*/ pasuje do liczby rze- NR liczba przeczytanych rekordów
czywistej ze znakiem; OFMT specyfikacja formatu dla liczb
/[A-Za-z]+/ pasuje do ciągu liter poprzedzo- OFS separator pól na wyjściu, por. s. 23
nych znakiem (np: komenda TEX-owa). ORS separator rekordów na wyjściu, por. s. 23
RLENGTH por. opis funkcji match, s. 16
RS separator rekordów
Wyrażenia RT napis pasujący do wyra enia RS, por. 21
Podstawą składni wyrażeń AWK-a jest popularna RSTART por. opis funkcji match, s. 16
SUBSEP separator indeksów tablic, por. s. 19
składnia wyrażeń języka C. Składnia ta została
w AWK wzbogacona o operacje tekstowe.
ENVIRON jest tablicą przechowującą wartości zmien-
Elementami wyrażeń są: stałe, zmienne, ope-
nych środowiskowych. Indeksami są nazwy zmiennych,
ratory, funkcje wbudowane i definiowane przez wartościami zaś napisy zawierające wartości tych zmien-
użytkownika oraz elementy tablic asocjacyjnych. nych. Przykładowo:
Omówimy je po kolei.
ENVIRON["TEXCONFIG"]
W AWK-u istnieją tylko dwa typy danych: licz-
bowy i napisowy. Zmienne liczbowe przechowują mo e zawierać, np: .;texdvips;gslibpsfonts
wartości zmiennopozycyjne, przy czym ich do- AWKPATH – nazwa zmiennej środowiskowej zawierają-
cej ście ki dostępu do katalogów zawierających programy
kładność zależna jest od implementacji. Zmienne
AWK-owe (czyli pliki *.awk) Je eli zmiennej AWKPATH nie
napisowe przechowują oczywiście ciągi znaków nadano adnej wartości (poleceniem SET w systemie DOS)
czyli napisy. Zmiennych nie deklaruje się. Typ to jest ona równa ".;c:/lib/awk;c:/gnu/lib/awk".
zmiennej określony jest przez kontekst; w razie ERRNO udostępnia napis zawierający systemowy komu-
potrzeby zawsze dokonywana jest odpowiednia nikat błędu, je eli przy wykonaniu funkcji getline lub
konwersja. Zmienna nie zainicjowana ma wartość close wystąpi błąd. Przykładowo, po wykonaniu:
zero lub "" (pusty napis). Stałe liczbowe zapisu- getline < "qq.qq.qq"
jemy jak w C, tj. 3.1415 lub 1.333e-5, stałe ERRNO zawiera "No such file or directory".
napisowe otacza się znakami ". IGNORECASE określa czy AWK rozró nia du e i małe
Istotny jest także sposób interpretacji wyra- litery przy porównywaniu napisów oraz wyra eń regular-
żeń numerycznych i tekstowych w operacjach nych. Je eli IGNORECASE jest niezerowe/niepuste, wtedy
logicznych. Fałsz odpowiada liczbie 0 i napisowi operatory ~, !~ oraz funkcje gensub, gsub, index,
match, split oraz sub nie rozró niają du ych i małych
pustemu "", zaś prawda odpowiada wszystkim
liter. Dotyczy to tak e wartości zmiennych wbudowanych
innym liczbom i napisom. RS i FS. GAWK począwszy od wersji 3.0 obsługuje normę
Zmienne wbudowane. Większość zmiennych zo- ISO-8859-1 (tj. ISO Latin-1). Standard ten nie zawiera
jednak większości polskich znaków diakrytycznych.
stała lub zostanie dokładnie omówiona przy okazji
ARGIND przechowuje indeks pod którym, w tablicy
omawiania tych aspektów AWK-a, których dotyczą. ARGV znajduje się nazwa przeglądanego pliku. Zawsze jest
Oto zestawienie wszystkich zmiennych: prawdziwa równość FILENAME == ARGV[ARGIND].
Zmienne wbudowane Uwagi: Zmienna FILENAME zawiera nazwę bieżą-
Zmienna Opis znaczenia cego pliku wejściowego. Oznacza to, że w obrę-
ARGC liczba argumentów wywołania bie wzorców BEGIN i END wartość FILENAME jest
programu nieokreślona.
ARGV tablica argumentów wywołania
programu Operatory arytmetyczne. Operatory arytmetyczne
ARGIND index w ARGV odpowiadający są naszym zdaniem łatwe i intuicyjne:
bie ącemu plikowi
ENVIRON tablica zmiennych środowiskowych Operatory arytmetyczne
ERRNO napis z systemowym opisem błędu
FIELDWIDTHS specyfikacja długości pól, por. s. 22 Postać wyrażenia Opis znaczenia
FILENAME nazwa bieżącego pliku wejściowego * iloczyn
FNR numer bieżącego rekordu + suma
w bieżącym pliku - różnica
FS separator pól / iloraz
IGNORECASE przełącznik rozró niania wysokości liter % modulo
NF liczba pól w bieżącym rekordzie ^ potęga
6. 1996 GUST, Zeszyt 7 15
++ inkrementacja Samotnie pojawiające się na przykład w ak-
– dekrementacja cji wyrażenie /TeX/ jest równoważne w AWK
wyrażeniu $0 ~ /TeX/.
Operatory napisowe. Napisy i zmienne napi- przykład 3
sowe można łączyć (konkatenować) przy po- Zaimplementujmy funkcję, zwracającą 1 jeżeli rok jest
mocy „niewidocznego” operatora – po prostu przestępny, lub 0 dla lat nieprzestępnych. Algorytm
należy umieścić napisy obok siebie. Przykładowo cytujemy za [3], s. 121.
po wykonaniu: function leapyear(year) {
return year %4 == 0 && year % 100
y = "Ali"; x = y "ba" "ba" != 0 || year%400 ==0; }
zmienna x ma wartość "Alibaba". Oprócz ope-
racji konkatenacji AWK nie ma żadnych innych BEGIN {print leapyear(1996),
leapyear(1806), leapyear(1066)}
operatorów napisowych.
Wzorzec BEGIN jest oczywiście potrzebny tylko dla
Operatory porównywania i operatory logiczne. testowania funkcji. Jeżeli powyższy kod umieścimy
Zapis i działanie operatorów w AWK-u w wypadku np. w pliku lyear.awk to pisząc gawk -f lyear.awk
zmiennych typu liczbowego jest identyczny jak otrzymamy na ekranie:
w języku C. Nowością AWK-a jest to, że mogą być 100
także stosowane do napisów. Uwagi: Bardzo długa instrukcja może zostać podzielona
i zapisana w kilku linijkach. Znakiem kontynuacji jest ,
Operatory porównywania bezpośrednio przed znakiem końca linii (por. drugą
linijkę przykładu). Jeżeli linijka kończy się przecinkiem
Operator Opis znaczenia
(por. linijka przedostatnia) to znak kontynuacji jest
== równe opcjonalny.
!= różne
< mniejsze Przypisanie. Przypisanie oznaczane jest w AWK-u
<= mniejsze lub równe
pojedynczym znakiem równości =. Podobnie jak
> większe
>= większe lub równe w języku C operator ten nadaje zmiennej wartość
i zwraca przypisaną wartość, stąd dozwolone są
Napisy są porównywane w taki sposób, że wyrażenia postaci x = y = 1 lub (x = y) <= 1.
najpierw porównywane są pierwsze znaki, po- Z operatorem przypisania związane są ope-
tem drugie itd. Przykładowo: "10" jest mniejsze ratory modyfikacji: +=, -=, *=, /=, %=, /= i ^=.
od "9". Jeżeli jeden napis jest przedrostkiem dru- Przykładowo wyrażenie x += y jest tożsame z x
giego to krótszy napis jest mniejszy od dłuższego, = x + y, wyrażenie x -= y jest tożsame z x = x -
np. Ali jest mniejsze od "Alibaba". Oczywiście y itd.
prawdą jest: "TeX" == "TeX". Operator warunkowy ?:. Operator warunkowy
Operatory logiczne ?: posiada następującą składnię:
Operator Opis znaczenia wyrażenie1 ? wyrażenie2 : wyrażenie3
&& suma logiczna Najpierw obliczane jest wyrażenie1 . Jeśli jest ono
|| alternatywa prawdziwe obliczane jest wyrażenie2 , w przeciw-
! zaprzeczenie nym wypadku wyrażenie3 .
Poniższy program oblicza i drukuje odwrot-
Operatory związane z dopasowywaniem wyrażeń ność pierwszych pól wszystkich rekordów, spraw-
regularnych. Ostatnia grupa to operatory zwią- dzając czy $1 nie jest równe zeru:
zane z dopasowywaniem wyrażeń regularnych, są {print $1!=0 ? 1/$1 : "Zero w linii", NR;}
one specyficzne dla języka AWK.
Mamy tylko dwa takie operatory ~ oraz
Arytmetyczne funkcje wbudowane
!~. Umożliwają one dopasowanie zmiennej do
pewnego wyrażenia regularnego. Przykładowo AWK oferuje inny zestaw funkcji wbudowanych
$1 ~ /TeX/ jest prawdziwe, gdy pierwsze pole niż język C. Funkcje wbudowane mogą być,
rekordu zawiera napis TeX. bez żadnych ograniczeń, elementami wyrażeń.
7. 16 GUST, Zeszyt 7 1996
Oto lista takich funkcji (niech x, y będą pewnymi wszelkiego rodzaju zmiany kontekstowe, por. przykład 5.
wyrażeniami): Symbol 0 oznacza całe wyra enie regularne r 5 .
Poni szy przykład wyjaśnia znaczenie argumentu a :
Funkcje arytmetyczne BEGIN{ t = "Alibababa";
Funkcja Wartość funkcji print gensub(/ba/, "BA", 2, t) }
atan2(y,x) arcus tangens z y/x w zakresia −π do π otrzymamy: AlibaBAba
cos(x) cosinus z x, x w radianach index( s , t )
sin(x) sinus z x, x w radianach Zwraca numer pierwszego znaku napisu t w napisie
exp(x) eksponent, czyli funkcja wykładnicza ex s . Jeżeli s nie zawiera t zwracana jest wartość zero.
int(x) część całkowita z x Pierwszy znak w napisie ma numer 1. Przykładowo:
log(x) logarytm z x przy podstawie e index("Alibaba", "baba")
sqrt(x) pierwiastek kwadratowy z x zwraca 4;
rand() przypadkowa liczba z przedziału 0, 1) length( s )
srand(x) x jest wartością początkową dla Podaje długość napisu s .
generatora liczb pseudolosowych
match( s , r )
Jeżeli s zawiera podnapis pasujący do r , to zwraca
Używając powyższych funkcji można uzy- numer pierwszego znaku tego podnapisu; w przeciw-
skać użyteczne liczby, na przykład π lub e; nym razie zwracane jest 0. Ponadto nadawane są war-
atan2(0, −1)= π oraz exp(1)= e. Również uzyska- tości zmiennym RSTART oraz RLENGTH. RSTART jest
nie logarytmu dziesiętnego nie jest problemem, równe wartości zwracanej przez funkcję, RLENGTH jest
równe długości podnapisu pasującego do r .
jeśli zastosujemy wzór log(x)/ log(10).
split( s , a , fs )
Podstawienie
Z napisu s tworzy tablicę napisów a w oparciu
randint = int(n * rand()) + 1 o znak separujący fs . Jeżeli split wywołamy tylko
spowoduje nadanie zmiennej randint wartości z dwoma parametrami to znakiem separującym jest
znak określony wartością zmiennej FS, czyli separator
z przedziału 1, n . pól w rekordzie.
sprintf( format , lista-wyra eń)
Napisowe funkcje wbudowane Zwraca napis, sformatowany według napisu format ,
por. funkcja printf, s. 22.
Poniższe zestawienie zawiera funkcje AWK-a umoż-
sub( r , s , t )
liwiające manipulowanie napisami. W zestawieniu Najdłuższy napis pasujący do wyrażenia regularnego
r oznacza wyrażenie regularne, s i t napis. r zamienia na napis s w napisie t (lub w $0 jeżeli
wywołana jest tylko z dwoma pierwszymi parametrami
Funkcje napisowe – podobnie jak gsub). Zwracana jest liczba dokonanych
gsub( r , s , t ) zamian.
Zamienia wyrażenie regularne r na napis s w napisie substr( s , p , n )
t . Zwracana jest liczba zamian. Jeżeli gsub wywołamy Zwraca napis wycięty z s począwszy od pozycji p
tylko z dwoma pierwszymi parametrami to zmiany o długości n znaków (lub do końca napisu s ,
dokonywane są w napisie $0. (tj. gsub( r , s ) jest jeżeli ostatni argument jest pominięty). Przykładowo
równoważne gsub( r , s ,$0)). wykonanie instrukcji
gensub( r , s , a , t ) print substr("Alibaba",4);
Uogólniona funkcja gsub. Zwraca zmieniony napis (nie spowoduje wydrukowanie napisu „baba”.
modyfikuje oryginalnego napisu t !). Zamienia wyra enie
regularne r w oparciu o napis s , w napisie t (je eli
nie ma t , domyślnym argumentem jest $0). Argument 5: W chwili pisania tego tekstu funkcja gensub jest zaim-
a określa, który z kolei podnapis pasujący do wyra enia plementowana z błędem. Mianowicie, je eli w tekście t
r ma być wymieniony. Je eli a jest napisem rozpoczy- nie ma napisu pasującego do r , to zwracana jest, zamiast
nającym się od "g" (lub "G") to wymieniane są wszystkie niezmienionego napisu t , liczba 5.9976e-315. Autorzy
napisy pasujące do r . Funkcja gensub umo liwia wsta- nie natknęli się na inne błędy, i w związku z tym wydaje
wienie fragmentów wyra enia regularnego w napisie s . się bezpieczne stosowanie tej funkcji połączone z te-
Je eli wyra enie r podzielimy za pomocą nawiasów, ( stowaniem zwracanej wartości, np. mo na zdefiniować
i ) na części składowe to te składowe mogą później poja- następującą funkcję xgensub:
wić się w napisie s (oznaczamy je jako n , gdzie n xgensub(r, s, a, t, tmp){
jest cyfrą od 1 do 9. Znaczenie tego jest takie, e napis tmp = gensub(r, s, a, t);
pasujący do n -tej składowej jest kopiowany, z tekstu t return tmp == 5.9976e-315 ? t : tmp
do tekstu zwracanego przez funkcję. W efekcie mo liwe są }
8. 1996 GUST, Zeszyt 7 17
tolower( s ) char = big[j]
Zwraca napis, w którym du e litery zostały zamienione na j = ALPHABET + 1;
małe. W wypadku tekstów polskich funkcja ta ma ogra- }}
niczone zastosowanie, nie zamieni bowiem liter z górnej newstring = newstring char;
połówki tabeli ASCII, gdzie znajdują się Ą, Ć, Ę, itd. }
toupper( s ) return newstring
Zwraca napis, w którym małe litery zostały zamienione na }
du e. Z „polskiego” punktu widzenia ma tę samą wadę co
tolower. BEGIN{ LOWER = "abcdefghijklmnopqrs
tuvwxyząćęłńóśź ";
UPPER = "ABCDEFGHIJKLMNO
przykład 4 (fragment spj.awk, pomysł M. Ryćko) PQRSTUVWXYZĄĆĘŁŃÓŚŹ ";
# wymień co w kontekście bef aft na na ALPHABET = length(LOWER);
function exch (bef, co, aft, na) {
while (match(para, bef co aft) > 0) { for (i = 1; i <= ALPHABET; i++){
match(para, bef co aft); little[i] = substr(LOWER,i,1)
nowy = substr(para, 1, RSTART) na big[i] = substr(UPPER,i,1)
substr(para, RSTART+RLENGTH-1); }
para = nowy; }
} Dodajmy jeszcze następujący wzorzec BEGIN:
}
BEGIN { print upper("Tó jęśt tęśtć"); }
Powyższa funkcja 6 realizuje kontekstową zamianę frazy
Uruchamiając AWK-a. Otrzymamy na ekranie:
na frazę. Jest namiastką tego czego AWK-owi do tej pory
brakowało (por. nast. przykład) – zamiany wyrażenia TÓ JĘŚT TĘŚTĆ
regularnego na wyrażenie regularne.
Zakładamy, że fraza bef jest jednoznakowa i po- Funkcje daty i czasu
przedza co , zaś fraza aft następuje po co i także jest
jednoznakowa. Przykład użycia: Interpreter GAWK od wersji 3.0 oferuje dwie funkcje doty-
czące daty i czasu. Są to systime i strftime:
{ exch("[0-9]","-","[0-9]", "–");
print } systime()
wymieni w całym tekście wszystkie frazy cyfra - cyfra Zwraca bie ący czas w sekundach jakie upłynęły od po-
na cyfra – cyfra czyli na przykład 1992-1993 zamieni czątku epoki. W standardzie POSIX jest to liczba sekund
na 1992–1993. od 1 Stycznia 1970 r.
strftime( format , czas )
przykład 5 (GAWK3.0) Zwraca napis zawierający czas (liczba w takim samym
Poni szy programik wykorzystujący funkcję gensub: formacie jak wartość zwracana przez systime) sformato-
{ $0=gensub(/([0-9])-([0-9])/, wany według specyfikacji zawartych w napisie format .
"1–2", "g",$0) Forma krótka strftime() oznacza u ycie formatu "%a
print %b %d %H:%M:%S %Z %Y" oraz bie ącego czasu. Forma
} strftime( format ) wypisuje bie ący czas według spe-
cyfikacji z formatu .
robi to samo co funkcja exch czyli zamienia w całym tek-
Specyfikacje przekształceń funkcji strftime są zgo-
ście wszystkie frazy cyfra - cyfra na cyfra – cyfra ,
dne ze standardem ANSI C. Ka da specyfikacja składa się
np. s.~1-11 zmieni na s.~1–11.
ze znaku „%” oraz następującego po nim znaku określa-
jącego typ konwersji (por. tak e funkcja printf, s. 22)
przykład 6 (zamiana małych liter na duże)
Nie będziemy podawać pełnej listy znaków konwersji
Poniższa funkcja jest odpowiednikiem funkcji toup-
(por. [4], s. 149–151), ograniczymy się do najczęściej
per; ma tę zaletę, że „rozpoznaje” polskie znaki. Ła-
stosowanych:
two też daje się modyfikować dla różnych wariantów
kodowania polskich znaków. Znaki konwersji
function upper(string, i, j){
Znak Typ przekształcenia
newstring = ""
for (i = 1; i <= length(string); i++){ d dzień miesiąca (01–31)
char = substr(string, i, 1) H godzina w zapisie 00–23
for (j = 1; j <= ALPHABET; j++){ I godzina w zapisie 01–12
if (char == little[j]){ j dzień roku (001–366)
m miesiąc (01-12)
M minuta (00–59)
6: Porównaj punkt Funkcje dalej w tekście. S sekundy (00–61)
9. 18 GUST, Zeszyt 7 1996
y rok w zapisie dwucyfrowym (00-99) break
Y rok w zapisie czterocyfrowym (np. 1066) natychmiastowe wyjście z pętli while, for, do
continue
kontynuacja iteracji w pętlach while, for, do
Instrukcje sterujące next
AWK pozwala nam grupować instrukcje, podejmo- rozpoczęcie następnej iteracji głównej pętli wejściowej 7
wać decyzje (konstrukcja if-else) oraz tworzyć exit wyrażenie
pętle (instrukcje for, while). Składnia tych sterowanie jest przekazywane bezpośrednio do akcji
END. Jeśli komendy exit użyto w akcji END, program
instrukcji pochodzi bezpośrednio z języka C.
kończy definitywnie działanie. Opcjonalne wyrażenie
Pojedyncza instrukcja może być zawsze za- zwracane jest jako status programu.
stąpiona listą instrukcji ujętych w nawiasy grupu-
nextfile
jące. Na liście instrukcje separowane są końcami zakończenie przeglądania bie ącego pliku i przejście do
linii lub średnikami. Znaki końca linii mogą poja- przeglądania następnego z podanych w linii komend, lub
wić się po dowolnym lewym i przed dowolnym (dla ostatniego pliku) przejście do wzorca END. W wy-
prawym nawiasem grupującym. niku wykonania nextfile zmienia się wartość zmiennej
Spójrzmy przykładowo na składnię instrukcji FILENAME, wartością FNR staje się jeden oraz wartość
ARGIND jest zwiększana o jeden.
if-else
if ( wyrażenie ) Instrukcja pusta. Jeśli w linijce programu AWK-
instrukcja1 -owego umieścimy samotny znak ‘;’, to otrzy-
else mamy instrukcję pustą. Spójrzmy na poniższy
instrukcja2 program wykorzystujący taką instrukcję w pę-
część else instrukcja2 jest opcjonalna. tli for; program drukuje wszystkie linie, które
W celu uniknięcia dwuznaczności przyjęto, zawierają puste pole.
że każdy else jest w parze z bezpośrednio BEGIN { FS = "t" }
poprzedzającym go if-em; przykładowo { for (i=1; i<=NF && $i-""; i++)
if (e1) if (e2) s=1; else s=2 ;
tu else jest w parze z drugim if-em. (Średnik if (i<=NF) { print }
po s=1 jest wymagany, gdyż else znalazł się }
w jednej linii z if-em.)
Tablice asocjacyjne
Instrukcje sterujące
Tablice asocjacyjne to jedyny rodzaj tablic do-
{ instrukcje }
grupowanie instrukcji stępny w AWK-u. Tablic nie trzeba deklarować,
określać ich wymiarów czy typu elementów skła-
if ( w ) instrukcja
jeśli wyrażenie w jest prawdziwe, wykonaj instrukcję dowych. Utworzenie elementu tablicy następuje
w chwili wykonania podstawienia, np. a[44]
if ( w ) instrukcja1 else instrukcja2
Wykonaj instrukcję1 jeśli wyrażenie w jest praw- = 3.14 lub a[1]="Alibaba", albo innego od-
dziwe, w wypadku przeciwnym instrukcję2 wołania do niego. Element nie zainicjowany jest
while ( w ) instrukcja
równy zero lub równy napisowi pustemu. Indeksy
jeśli wyrażenie w jest prawdziwe, wykonaj instrukcję nie są liczbami ale napisami. Użycie w kontekście
i powtórz indeksu liczby spowoduje jej konwersję do od-
for ( w1 ; w2 ; w3 ) instrukcja powiedniego napisu. Trzeba o tym pamiętać, np.
równoważne instrukcji: print a["01"] nie spowoduje wydrukowania
w1 ; while ( w2 ) { instrukcja ; w3 } słowa Alibaba (tylko przypuszczalnie napis pu-
for ( zmienna in tablica ) instrukcja sty) – napisy "1" oraz "01" są oczywiście różne,
instrukcja jest wykonywana dla zmiennej przyjmującej podczas gdy liczby nie.
kolejno wartości każdego elementu tablicy
do instrukcja while ( w ) 7: Przez główną pętlę wejściową rozumiemy mecha-
wykonywana jest instrukcja jeśli wyrażenie w jest nizm AWK-a do analizowania rekord po rekordzie pliku
prawdziwe i powtórz wejściowego.
10. 1996 GUST, Zeszyt 7 19
Poniższy program zapamiętuje wszystkie sło- for (i=1; i<=10; i++)
wa pliku wejściowego oraz liczbę ich wystąpień. for (j=1; j<=10; j++)
{for (i=1; i<=NF; i++) {ls[$i]++}} r[i,j] = rand();
Zwróćmy uwagę, że za każdym razem, gdy poja- zostanie utworzona tablica 100 elementów, do
wia się nowy wyraz, AWK tworzy nowy element których możemy się odwoływać za pomocą par
tablicy z wartością początkową 0. Następnie ope- zmiennych indeksowanych postaci i,j. Wewnętrz-
rator inkrementacji nadaje mu wartość 1. Każde nie jednakże poszczególne elementy tablic są in-
następne pojawienie się tego wyrazu powoduje deksowane za pomocą napisów postaci i SUBSEP
zwiększenie wartości już istniejącego elementu. j. Zmienna wbudowana SUBSEP przechowuje
Powstaje problem jak dobrać się do poszcze- znak używany do oddzielenia indeksów składo-
gólnych elementów tablicy ls, skoro nie znamy wych; standardową wartością tej zmiennej nie jest
wartości indeksów (wyrazów tekstu). Do tego celu przecinek ale znak "034". Sposób testowania czy
służy specjalna forma pętli for: element i,j należy do tablicy nie zmienia się:
for ( zmienna in tablica ) for ((i,j) in r) {...}
instrukcja zaś w wypadku pętli for piszemy:
zmienna przyjmuje iteracyjnie wszystkie warto- for (k in r) {print r[k]}
ści indeksów tablicy . Kolejność przeglądania
tablicy nie jest ustalona i jest zależna od kon- Uwagi: Elementy tablic nie mogą być tablicami.
kretnej implementacji AWK-a. Działanie pętli jest
nieokreślone jeżeli wewnątrz pętli zostaną dodane Funkcje
kolejne elementy do tablicy . Chcąc wydrukować AWK umożliwia definiowanie własnych funkcji.
listę słów z naszego przykładu możemy posłużyć Definicja funkcji może być umieszczona w do-
się następującą akcją z wzorcem END: wolnym miejscu programu, pomiędzy kolejnymi
END {for (word in ls) parami wzorzec-akcja. Funkcje są definiowane
print word, ls[word] następująco:
} function nazwa ( lista-argumentów ) {
Wyrażenie lista-instrukcji
indeks in tablica }
pozwala ustalić czy określony indeks występuje lista-argumentów to ciąg oddzielonych przecin-
w tablicy . Jeżeli występuje to wartością wyrażenia kami argumentów funkcji. Podczas wywołania
jest 1, w wypadku przeciwnym 0. Przykładowo, funkcji, argumentom nadawane są odpowied-
poniższa instrukcja sprawdza czy w tablicy ls nie wartości. Nazwy argumentów są lokalne
wystąpiło słowo TeX: dla funkcji; są one przekazywane przez war-
if ("TeX" in ls) {print "OK!"} tość, z wyjątkiem tablic, które są przekazywane
else {print "KO!"} „przez referencję”. Argumenty funkcji są jedynymi
zmiennymi lokalnymi w AWK-u.
delete. Element tablicy możemy usunąć za po- Lista-instrukcji może zawierać instrukcje re-
mocą instrukcji: turn wyrażenie . Wykonanie return polega na
delete tablica [ indeks ] obliczeniu wartość wyrażenia , a następnie prze-
kazaniu tej wartości w miejsce wywołania funkcji
przykładowo delete ls["TeX"] usuwa z tablicy
(tzw. wartość zwracana przez funkcję). Wyrażenie
ls element odpowiadający indeksowi "TeX".
jest opcjonalne – jeżeli go nie ma, instrukcja
Tablice wielowymiarowe. Tablice wielowymia- return jedynie przekazuje sterowanie do miej-
rowe są symulowane przez AWK-a za pomocą sca wywołania. Jeżeli wśród listy-instrukcji nie
tablic jednowymiarowych. Z punktu widzenia ma return to po wykonaniu ostatniej instrukcji
użytkownika nie ma to wielkiego znaczenia. (przed zamykającym nawiasem klamrowym) ste-
Przykładowo w wyniku działania poniższego rowanie jest przekazywane do miejsca wywołania,
fragmentu programu: a wartość zwracana jest nieokreślona. Zilustrujmy
11. 20 GUST, Zeszyt 7 1996
to prostym przykładem funkcji max zwracającej Jeżeli nie podamy pliku wejściowego to AWK bę-
większy ze swoich dwu argumentów ([1], s. 53): dzie czekał na strumień danych ze standardowego
function max(x, y) { wejścia (klawiatury). Często jest to działanie nie-
return x > y ? x: y zamierzone – ^C, ^break czy ^Z kończą działanie
} programu w takiej sytuacji.
Funkcje zdefiniowane za pomocą polece- Pola. Standardową wartością wbudowanej zmien-
nia function mogą być użyte w dowolnym nej FS jest " " (spacja – odstęp). W takiej sytuacji
wyrażeniu, a także wewnątrz innych funkcji; poszczególne pola są rozdzielone odstępami lub
dozwolona jest także rekursja (por. przykład 7). znakami tabulacji. Sposób rozdzielania pól można
Przy wywołaniu funkcji nie można umieszczać od- zmienić przypisując zmiennej FS odpowiedni na-
stępu pomiędzy jej nazwą a rozpoczynającym listę pis. Jeżeli napis ten jest dłuższy niż jeden znak to
argumentów nawiasem (. AWK traktuje go jako wyrażenie regularne. Najdłuż-
Jak już mówiliśmy tylko argumenty funcji są sze (leftmost longest) ciągi znaków, nie zachodzące
zmiennymi lokalnymi. Wszystkie inne zmienne na siebie (non overlaping), pasujące do tego wyraże-
są globalne. Jeżeli chcemy aby AWK „widział” ja- nia regularnego będą odzielać poszczególne pola
kąś zmienną tylko lokalnie to jedyną metodą w bieżącej linii. Przykładowo deklaracja:
jest umieszczenie jej na liście parametrów przy BEGIN {FS ="[,;:]"}
definiowaniu funkcji. Po prostu nadmiarowe pa- powoduje, że pola będą rozdzielane przecinkiem,
rametry umieszczamy na końcu listy. Nie będą średnikiem lub dwukropkiem. Kiedy wartością FS
one wykorzystywane do przekazywania wartości, jest pojedynczy znak (inny od odstępu) to ten znak
lecz będą stanowić dodatkowe zmienne lokalne. jest używany do rozdzielania pól.
Wywołanie funkcji z mniejszą od deklarowa- Uwagi: Wartość zmiennej FS może zostać nadana
nej liczbą parametrów jest w AWK poprawne także z poziomu uruchomienia AWK-a za pomocą
– wszystkie nadmiarowe parametry przyjmują przełącznika -F. Przyładowo zamiast powyższego
wartości zerowe. wzorca BEGIN moglibyśmy napisać w linii komend
przykład 7 (system DOS):
Następująca funkcja rekurencyjna odwraca napis poczy- gawk -F[,;:] -f prog.awk plik.we
nając od znaku s ([4], s. 151).
function rev(str, s) { Rekordy. Wartość zmiennej RS przechowuje znak
if (s == 0) używany przez AWK-a do oddzielania poszczegól-
return "" nych rekordów. Standardowo rekordy są oddzie-
return (substr(str, s, 1) rev(str, s-1))
lane znakami końca linii (co odpowiada przypi-
}
saniu RS="n"). W ograniczony sposób możemy
Dla wypróbowania dopiszmy następujący prosty pro-
zmienić sposób w jaki AWK wyróżnia poszczególne
gram:
rekordy, nadając odpowiednią wartość zmien-
BEGIN {print rev("Vrooom",length("Vrooom"))}
nej RS. W opisie [1] separatorem rekordu może
Zakładając, że funkcja rev i wzorzec BEGIN znajdują
być tylko napis jednoznakowy lub napis pusty.
się w pliku rev.awk piszemy teraz gawk -f rev.awk.
Na ekranie powinno się pojawić: Jeżeli RS="" (napis pusty) wtedy, separatorami
rekordów są puste linie (jedna lub więcej).
mooorV
przykład 8 Wstawianie tyld (podejście naiwne)
W zadaniu wstawienie tyld po spójnikach naturalnym
Wejście wydaje się podejście, przy którym rekordem jest cały
akapit (odpada problem spójników kończących linijkę):
AWK może czytać dane wejściowe na kilka sposo-
BEGIN {RS=""; FS="n"; }
bów. Najprostszym jest uruchomienie go w stan-
{ gsub(/[ t]+i[ t]*n/,"ni ");
dardowy sposób czyli, np: gsub(/ni[ t]+/,"ni~");
gawk -f prog.awk plik.we gsub(/[ t]+i[ t]+/," i~");
itd. dla wszystkich spójników
W takim kontekście, zgodnie z tym co już napisano print;
we wstępie, AWK czyta plik plik.we linia po linii. }
12. 1996 GUST, Zeszyt 7 21
Wadą tego podejścia jest to, że długie akapity mogą getline. Funkcja getline umożliwia czytanie da-
przekroczyć możliwości pamięciowe AWK-a. nych z bieżącego lub/i z innego pliku tekstowego
Począwszy od wersji 3.0 GAWK-a, separator rekordu albo z potoku generowanego przez inny program.
mo e być wyra eniem regularnym. W takiej sytuacji, ka dy Poniżej zestawiono różne formy użycia getline.
napis pasujący do tego wyra enia wyznacza koniec kolej-
nego rekordu (z tym, e napis ten nie jest częścią tego getline
rekordu, podobnie jak w wypadku gdy separatorem jest Postać instrukcji Inicjalizowane zmienne
pojedynczy znak. getline $0, NF, NR, FNR
Je eli RS jest wyra eniem regularnym wtedy zmienna getline z z , NR, FNR
RT przechowuje dla bie ącego rekordu, napis będący jego getline < plik $0, NF
separatorem od rekordu następnego. getline z < plik z
program | getline $0, NF
przykład 9 (edytor potokowy) program | getline z z
Następujący program ([4], s. 240–241) jest AWK-ową
implementacją edytora potokowego, tj. takiego programu, plik i program to zmienna lub stała napisowa zawiera-
który czyta strumień danych, modyfikuje go i wysyła jąca odpowiednio nazwę pliku lub programu z którego
dalej. Poni sza implementacja wyró nia się oryginalnością AWK ma czytać strumień danych.
pomysłu. Sposób u ycia jest następujący:
Dwie pierwsze formy dotyczą czytania da-
gawk -f awksed.awk co naco plik1 plik2 ...
nych z bieżącego pliku, dwie następne z pliku .
Spowoduje zastąpienie frazy (wyra enia regularnego) co Dwie ostatnie to wczytywanie danych ze strumie-
na napis naco (oba argumenty są wymagalne), w plikach nia generowanego przez inny program . Funkcja
plik1 plik2 .... Je eli nie podamy listy plików dane
getline zwraca wartość 1 jeżeli wczytana została
będą czytane ze standardowego wejścia.
następna linia tekstu (rekord), 0 jeżeli napotkano
# A. Robbins, arnold@gnu.ai.mit.edu koniec pliku (strumienia) danych oraz −1 w wy-
# Thanks to Michael Brennan for the idea
# August 1995
padku napotkania błędu (np. otwarcia pliku). Je-
żeli po słowie getline występuje zmienna z
function usage() { to wczytana linia jest dostępna jako wartość tej
print "usage: awksed pat repl files..." > "con" zmiennej, w innym wypadku jest dostępna jako
exit 1 wartość zmiennej $0. Przykładowo pętla:
}
BEGIN { while (getline < plik > 0) {...}
# validate arguments Jest „tradycyjną” techniką umożliwiającą przejrze-
if (ARGC < 3) { usage() } nie całego pliku . Poszczególne linie dostępne są
RS = ARGV[1]; ORS = ARGV[2]
w każdej iteracji pętli jako wartości zmiennej $0.
# don't use arguments as files Podobnie wygląda czytanie danych z potoku.
ARGV[1] = ARGV[2] = "" Przykładowo chcąc przekazać do AWK-a zawar-
} tość bieżącego katalogu możemy się posłużyć
następującą pętlą (dla system DOS):
{
if (RT == ""){ printf "%s", $0 } while ("dir/b *.*" | getline > 0) {...}
else { print } Pliki i potoki otwarte przez AWK-a są automatycz-
} nie zamykane z chwilą zakończenia działania pro-
Idea działania jest prosta: separatorem rekordów jest co gramu. Jeżeli jednak musimy przeglądać wielo-
a separatorem rekordów na wyjściu naco . Problemem krotnie zawartość jakiegoś pliku w obrębie jed-
jest jedynie sytuacja, w której ostatni rekord nie kończy się nego programu AWK-owego to za każdym razem
napisem pasującym do RS. Je eli plik nie kończy się napi-
należy zamknąć czytany plik używając instrukcji
sem pasującym do RS to zmienna RT będzie równa napi-
sowi pustemu. Stąd, warunek if (RT=="")... gwaran- close, np.
tuje wydrukowanie całej zawartości pliku wejściowego. close ( plik ); close("dir /b *.*")
Drugi ciekawy fragment tego przykładu to przypisa-
nie ARGV[1] = ARGV[2] = "". Chodzi o to, eby AWK nie przykład 10
traktował napisów co i naco jako nazw plików wejścio- Poniższy program po uruchomieniu wyświetli listę
wych. Dokładne wyjaśnienie znaczenia tej linijki znajduje wszystkich plików, których wielkość jest większa od
się w punkcie Argumenty wywołania programu, s. 24. SIZE.
13. 22 GUST, Zeszyt 7 1996
BEGIN { zawierającego ciąg oddzielonych odstępami liczb. Ka da
flag = 1 liczba oznacza długość odpowiedniego pola w znakach.
SIZE = 1000000 Je eli wartością zmiennej FIELDWIDTHS nie jest napis pu-
while ("dir /s/a-d" | getline) { sty, pola wyznaczane są w oparciu o specyfikację podaną
gsub(/./,""); w tej zmiennej, a nie w oparciu wartość separatora pól
if ($NF ~/[0-90-9]:[0-90-9]/ && $(NF-2)> SIZE) (czyli zmienną FS). Przypisanie wartości zmiennej FS (np.
{print $0; flag =0} FS=FS) przywraca standardowy sposób wyznaczania pól.
} Zwróćmy uwagę, e GAWK nie dokonuje adnego
sprawdzenia poprawności specyfikacji podanej w napisie
if (flag) FIELDWIDTHS
{print "NO FILES BIGGER THAN", SIZE; }
} przykład 12
Następujący program wyświetli listę wszystkich plików,
Uwagi: Ponieważ DOS wyświetla wielkość plików z krop-
których wielkość jest większa od SIZE. Lista plików ge-
kami „księgowymi” przed każdymi trzema cyframi, tj. na
nerowana poleceniem dir ma zmienną liczbę pól w zale -
przykład 200.124 pozbywamy się ich za pomocą funk-
ności od tego czy nazwa pliku ma czy nie ma rozszerze-
cji gsub. Instrukcja if najpierw testuje czy wczytana
nie (pomijamy na razie nagłówek i katalogi). W przykła-
linia zawiera ostatnie pole składające się z czterch cyfr
dzie 10 liczyliśmy pola od końca. Posługując się zmienną
z dwukropkiem po pierwszej parze (czas ostatniej mo-
dyfikacji pliku). Ta sztuczka pozwala „odcedzić” pola FIELDWIDTHS mo emy rozwiązać problem inaczej:
nie zawierające informacji o plikach (nagłówek listy BEGIN {FIELDWIDTHS = "8 1 3 14 1 8 3 5";
i statystykę zbiorczą). Następnie sprawdzany jest wa- BEGIN { SIZE = 1000000 }
runek czy wielkość pola jest większa od SIZE. Pola
liczymy od końca gdyż tylko wtedy możemy jedno- { gsub(/./,"",$0); }
znacznie ustalić, która kolumna zawiera wielkość pliku
(druga od końca). $0 !~ /<DIR>/ && $1 !~ /^ / && $4 > SIZE {
printf "%s %s %dn", $1, $3, $4;
przykład 11 }
Poniższy funkcja pobiera bieżącą datę z komputera Program uruchamiamy w „egzotyczny”, jak na system
i udostępnia ją w trzyelementowej tablicy CDATE, której DOS, sposób (zakładając, e powy szy kod znajduje się
element "year" zawiera rok, element "mon" – miesiąc w chksize.awk):
a element "day" – dzień.
dir | gawk -f chksize.awk
function getdate(x,date) {
"echo. | date" | getline x;
gsub("-", " ", x); Instrukcje wyjścia – print/printf
split(x, date, " ");
CDATE["year"]= date[5]; Instrukcje print oraz printf służą do druko-
CDATE["mon"]= date[6]; wania. Pierwsza z nich drukuje swoje argumenty
CDATE["day"]= date[7]; zawsze według tego samego formatu, druga
return;
}
umożliwia precyzyjniejsze sterowanie postacią wy-
Uwagi: "echo. | date" z góry odpowiada DOS-owi na pisywanych danych. Dane mogą być drukowane
jego durne pytanie Enter new date... (inaczej pro- na ekran, do pliku lub do potoku.
gram będzie czekał na naciśnięcie enter). Argumenty
printf. Składnia instrukcji printf jest niemalże
x, i date nie służą do przekazywania wartości, ale do
uczynienia obu zmiennych lokalnymi (por. rozważania identyczna z odpowiednią funkcją języka C.
na ten temat w punkcie: Funkcje) – funkcję wywołujemy Ogólna postać tej instrukcji jest następująca:
po prostu getdate() 8 . printf format , arg1 , arg2 ,...
Pola o ustalonej długości. Rekord mo e być tak e lub
dzielony na pola o ustalonej długości. W tym celu zmien-
nej wbudowanej FIELDWIDTHS nadajemy wartość napisu printf ( format , arg1 , arg2 ,...)
Napis lub zmienna napisowa format określa spo-
8: Zwracamy uwagę, że program jest „nieodporny” na sób przekształcania i formatowania argumentów.
zmianę formatu daty, co w DOS-ie ma miejsce przy uak- Zawiera on dwa rodzaje obiektów: zwykłe znaki,
tywnieniu innej strony kodowej. Bardziej inteligentna kopiowane po prostu przy drukowaniu oraz spe-
wersja może rozpoznawać która liczba jest dniem, która
miesiącem a która rokiem po zawartości drugiej linii
cyfikacje przekształceń z których każda określa
drukowanej przez polecenie date, tj. tekstu: Enter sposób przekształcenia i wypisania kolejnego argu-
new date (yy-mm-dd). mentu funkcji printf. Specyfikacja ta rozpoczyna
14. 1996 GUST, Zeszyt 7 23
się od znaku % a kończy znakiem określającym typ %-.3s Alibaba |Ali|
konwersji. Pomiędzy tymi znakami możemy użyć %-9.3s Alibaba |Ali |
ponadto następujących znaków modyfikujących:
-, zawartość pola jest justowana do lewego print. Instrukcja print jest uproszczoną formą
krańca pola; printf a jej działanie jest następujące: druko-
ciąg-cyfr , określający minimalny rozmiar pola wane argumenty są oddzielane znakiem usta-
(w znakach). Przekształcony argument będzie lonym jako wartość zmiennej wbudowanej OFS
wpisany do pola o długości co najmniej równej (standardowo OFS=" " – argumenty odzielone są
ciąg-cyfr . Jeżeli argument składa się z mniej- znakiem odstępu); na końcu listy jest drukowany
szej liczby znaków, to będzie ono uzupełnione znak ustalonym jako wartość zmiennej wbudowa-
do długości minimalnej znakami odstępu. Je- nej ORS (standardowo ORS="n" – każde kolejne
żeli specyfikacja długości pola rozpoczyna print drukuje od nowego wiersza). Wszystkie ar-
się cyfrą 0, to pole będzie wypełniane nie gumenty są drukowane w oparciu o tę samą specy-
znaczącymi zerami; fikację, określoną poprzez wartość zmiennej OFMT
. ciąg-cyfr , maksymalna szerokość pola (dla (standardowo OFMT="%.6g"). Stąd, uruchamiając
napisu) lub liczba cyfr po kropce dziesiętnej. poniższy przykład:
Oto lista znaków przekształceń i ich znaczenie: BEGIN {OFS=":";ORS="->";
print log(2), log(3); print log(5);
Znaki konwersji }
Znak Typ przekształcenia otrzymamy
c znak 0.693147:1.09861->1.60944->
d liczba całkowita
Uwagi: print to skrót od print $0 (a nie jak
e liczba postaci [-]d.ddddddE[+-]dd
f liczba postaci [-]ddd.dddddd można by się spodziewać print ORS).
g e lub f, w zależności od tego które jest krótsze Drukowanie do plików. Zamiast na ekran (stan-
przy czym nieznaczące zera nie są drukowane
dardowe wyjście) instrukcje printf/print mogą
o liczba ósemkowa bez znaku
s napis przesłać dane do pliku. Służą do tego operatory >
x liczba szestnastkowa bez znaku oraz >>, zaś instrukcja mają wówczas postać:
printf format , arg1 ,... > plik
Jeżeli znak występujący po % nie jest znakiem prze-
print arg1 ,... > plik
kształcenia to jest on po prostu wypisany; zatem %%
spowoduje wypisanie znaku %. lub
printf format , arg1 ,... >> plik
Poniższe zestawienie ilustruje działanie róż- print arg1 ,... >> plik
nych specyfikacji. Aby można ocenić długości plik jest napisem lub zmienną typu napiso-
pól otoczono je znakami |, zaś spacje oznaczono wego zawierającą legalną (z punktu widzenia
znakiem . systemu operacyjnego) nazwę pliku. Przykładowo
program:
Przykłady specyfikacji
{ printf "%sn", $0 > $1 }
Specyfikacja Argument Wynik
będzie działał doputy, dopóki wartość pierwszego
%5d%% 33.33 | 33%|
pola w pliku wejściowym zawiera napis mogący
%c 33.33 |!|
%d 33.33 |33| być legalną nazwą pliku (w systemie UNIX byłby
%5d 33.33 | 33| problem jeżeli $1 zawierałoby dla którejś linii pliku
%e 3.1415 |3.141500e+000| np. frazę Procter&Gamble).
%f 3.1415 |3.141500| Uwagi: Instrukcja printf "%d %dn", $1, $2
%8.3f 3.1415 | 3.141| > $3 spowoduje wydrukowanie $1 i $2 do
%08.3f 3.1415 |0003.141|
%s Alibaba |Alibaba| pliku określonego jako zawartość pola $3, a nie
%9s Alibaba | Alibaba| $1 oraz wyniku porówania wartości drugiego
%-9s Alibaba |Alibaba | i trzeciego pola. Jeżeli chcemy osiągnąć to drugie
15. 24 GUST, Zeszyt 7 1996
to powinniśmy napisać: printf "%d %dn", $1, system. Instrukcja system ma postać:
($2 > $3) albo printf ("%d %dn", $1, $2 system( komenda )
>$3).
wykonuje komendę systemową przekazaną jej
Operator > otwiera plik tylko raz (niszcząc
jako wartość napisowego argumentu komenda .
poprzednią zawartość); kolejne instrukcje printf
Najczęściej funkcje printf/printf i operatory
dodają tekst do tego pliku. Operator >> różni
>, >> i | wystarczają do zrealizowania typowych
się od > tym, że przy otwarciu pliku poprzednia
zadań bez potrzeby uciekania się do komendy
zawartość nie jest niszczona.
system.
Drukowanie w potoku. Możliwe jest także dru-
kowanie w potoku za pomocą instrukcji printf Argumenty wywołania programu
(print) o postaci:
Wartości argumentów wywołania programu są,
printf format , arg1 ,... | program podobnie jak w wypadku języka C, przechowy-
print arg1 ,... | program wane we wbudowanej zmiennej tablicowej ARGV.
program jest napisem lub zmienną napisową Zmienna ARGC zaś zawiera liczbę argumentów.
zawierającą nazwą programu-odbiorcy strumienia Przykładowo:
danych drukowanych przez printf (print).
gawk -f 1.awk test.txt v=1 b
Rozważmy prosty program drukujący z pliku
wejściowego linie zawierające więcej niż 9 pól. Nic ARGC ma wartość 4, ARGV[0] ="gawk", ARGV[1]
prostszego jak zapisać NF > 9{print }. Jednakże ="test.txt", ARGV[2] ="v=1", ARGV[3] ="b".
gdy liczba linii spełniających ten warunek jest Jak widzimy argumenty zdefiniowane za po-
duża to na ekranie zobaczymy tylko dwadzieścia mocą opcji -f nie są liczone (podobnie -v) ale
parę ostatnich, a reszta mignie nam przed oczami. za to ARCV[0] jest równa nazwie programu,
W systemie DOS możemy usunąć tę niedogodność który uruchomiliśmy (tu „gawk”). Poniższy pro-
pisząc 9 : gram wypisuje wartość wszystkich argumentów
wywołania:
NF > 9 {print | "more"}
BEGIN {for (i=0; i< ARGC; i++)
close. Instrukcja close służy do zamknięcia {print ARGV[i]}
otwartego za pomocą operatorów >, >> i |. Jej }
ogólna postać jest następująca: Zmienne ARGC i ARGV mo na zmieniać wewnątrz pro-
close( napis ) gramu. Po napotkaniu końca bie ącego pliku wejściowego
AWK pobiera następny element tablicy ARGV jako nazwę
gdzie napis jest identyczny z napisem plik lub następnego pliku wejściowego. Przykładowo:
program , za pomocą których uprzednio otwarto BEGIN{ ARGV[1]="test.txt";
plik/potok. Instrukcja ta jest niezbędna w sytuacji if(ARGC < 2) {ARGC = 2} }
gdy np. najpierw piszemy do pliku a następnie spowoduje, e AWK, zawsze będzie przeszukiwał jako
chcemy (w obrębie tego samego programu) czytać pierwszy plik „test.txt” bez względu na to jaki (i czy
z niego dane. w ogóle) podamy w linii komend.
Aby usunąć nazwę pliku z tablicy ARGV mo emy albo
fflush. Instrukcja fflush o postaci nadać odpowiedniemu elementowi tablicy wartość napisu
fflush( napis ) pustego, albo posłu yć się poleceniem delete. Napis
opró nia bufor związany z jakimś plikiem lub potokiem (po- „-” oznacza standardowe wejście. Tego typu manipula-
przez napis , identyczny z napisem plik lub program cje z reguły umieszczamy wewnątrz wzorca BEGIN, por.
za pomocą których uprzednio otwarto plik/potok). Do- przykład 9.
puszczalne są tak e dwie następujące postacie instruk- Poniewa nie wiemy na ile omówione tutaj mo liwo-
cji: fflush() oraz fflush(""). Pierwsza opró nia bu- ści manipulowania zmiennymi ARGV i ARGC są przenośne
for związany ze standardowym wyjściem, druga bufory dla innych implementacji AWK-a na wszelki wypadek ozna-
związane z wszystkimi otwartymi w danej chwili pli- czyliśmy je jako rozszerzenia GAWK-a pismem pochyłym.
kami/potokami.
przykład 13
W przykładzie 10 wielkość pliku była zadeklarowana na
9: Przykład trochę naciągany gdyż to samo można stałe co jest pewną niedogodnością, poniższa modyfi-
osiągnąć pisząc w linii komend gawk "NF > 9 kacja pozbawiona jest już tej wady. Chcąc uzyskać listę
{print }" | more. plików np. większych od 500kb, wystarczy teraz napisać
16. 1996 GUST, Zeszyt 7 25
gawk -f chkbig.awk 500 (gdzie chkbig.awk zawiera
poniższy kod).
BEGIN {
flag = 1
if (ARGC > 0) {SIZE = ARGV[1] * 1000}
else { SIZE = 1000000} # default
dalej jak w przykładzie 10
}
Uruchamianie AWK-a. Do tej pory uruchamiali-
śmy AWK-a pisząc:
gawk -f prog.awk plik.we plik.we ...
Jest to sposób stosowany najczęściej, ale nie je-
Rys. Miejsce AWK w TEX-owym środowisku składu
dyny. Jeżeli program AWK-owy jest bardzo krótki
można napisać po prostu 10 : Jeszcze wygodniejsze będzie przygotowanie odpowied-
gawk " instrukcje " plik.we niego skryptu bat, np. chkbig.bat:
np. @echo off
if %1.==. goto STANDARD
gawk "NF != 5{print $0}" plik.we gawk -vSIZE=%1000 -f chkbig.awk
AWK można wywołać z kilkunastoma opcjami. Naj- goto END
ważniejsze z nich to -f, -F oraz -v. Dwie pierw- :STANDARD
sze już omówiliśmy, ostatnia umożliwia nadanie gawk -vSIZE=500000 -f chkbig.awk
:END
zmiennej 11 wartości w momencie uruchomienia
Żeby otrzymać listę plików większych od np. 600kb
programu (z linii komend). Przykład 14 ilustruje
wystarczy teraz napisać w linii komend:
sposób wykorzystania opcji -v.
chkbig 600
Uwagi: Z czasem gdy dorobimy się biblioteki wła-
snych funkcji AWK-owych, może powstać problem,
jak w elegancki sposób dołączać ją do róż- Podsumowanie
nych plików, w taki sposób, jak umożliwia to
W artykule przedstawiono opis standardu AWK,
instrukcja input w TEX-u czy instrukcja #in-
oraz rozszerzenia tego języka oferowane przez in-
clude w C? Okazuje się, że opcja -f nie musi
terpreter GAWK. Przykłady przedstawione w arty-
wcale występowąć tylko raz:
kule oraz kilkanaście innych nie zaprezentowa-
gawk -f mylib.awk -f prog.awk plik.we
nych z braku miejsca są dostępne w archiwum
Gdzie plik mylib.awk zawiera bibliotekę naszych GUST-u (ftp.GUST.org.pl). Tam też znaleźć
funkcji. Nie jest to rozwiązanie idealne, ale według można dystrybucję GAWK-a (zawiera [4]), oraz [2].
wiedzy autorów, najlepsze z możliwych.
przykład 14 Bibliografia
Przykład 10 można zmodyfikować w inny sposób niż ten
zaprezentowany w przykładzie 13. Wystarczy, że przy [1] Aho A.V., Kernighan B.W., Weinberger P The
.J.,
uruchamianiu pliku chk_big.awk będzie nadawana AWK Programming Language, Addison-Wesley
odpowiednia wartość zmiennej SIZE (oczywiste zmiany 1988.
w pliku chkbig.awk pozostawiamy czytelnikom), np: [2] Aho A.V., Kernighan B.W., Weinberger P .J.,
gawk -vSIZE=500000 -f chkbig.awk AWK – A Pattern Scanning and Processing Lan-
guage, dostępny m.in. w ftp.GUST.org.pl
10: W systemie UNIX, zamiast cudzysłowów ma- jako plik postscriptowy, 1978.
szynowych używamy cudzysłowów pojedynczych, [3] Kernighan B.W., Ritchie D.M., Język C, WNT
tj. gawk ' instrukcje ' plik.we . 1988.
11: Ogólnie zmiennym, ponieważ możemy ją [4] Robbins A.D., A User’s Guide for GNU AWK,
używać wielokrotnie. version 3.0, January 1996.