Az AWK programozási nyelv

Vezérlési szerkezetek

Utasítások

Egy utasításnak egy sorban kell elhelyezkednie, de a ’;’ is elválaszthatja az utasításokat egymástól Pl.:

BEGIN { print " Ezt egyszer írja ki \n" )

Reguláris kifejezések

A reguláris kifejezések, vagy regexp, szavak (betűk bizonyos halmazának) leírására alkalmasak. Mivel a reguláris kifejezések alapvető részei az awk programozási nyelvnek, ezért egy önálló fejezetben tárgyaljuk őket.

A `/' karakterek közé tett reguláris kifejezések szolgálnak awk mintaként és illeszkednek minden olyan sorra, amelyik az adott halmazhoz tartozik.

A legegyszerűbb reguláris kifejezés betűk és/vagy számok sorozata. Ezek a reguláris kifejezések minden olyan szóra illeszkednek, amelyek tartalmazzák az adott sorozatot. Így a `foo' reguláris kifejezés illeszkedik minden olyan szóra ami tartalmazza `foo'-t. Továbbá a /foo/ minta illeszkedik minden olyan bemeneti sorra amelyben a `foo' karaktersorozat, bárhol a sorban, előfordul. Más reguláris kifejezések lehetővé teszik bonyolultabb betűhalmazok definiálását.

Kezdetben a példák egyszerűek lesznek, de ahogy többet mondunk el arról, hogyan működnek a reguláris kifejezések, úgy egyre bonyolultabb példákat mutatunk.

Hogyan használjuk a reguláris kifejezéseket

Egy reguláris kifejezés mintaként használható ha `/' karakterek közé tesszük. Ebben az esetben egy reguláris kifejezés minden sor teljes szövegével összehasonlításra kerül. (Általában a szövegnek csak egy részéhez kell illeszkednie, hogy az összehasonlítás sikeres legyen.) Például ez a program kinyomtatja minden olyan sor második mezőjét, amely bárhol tartalmazza a `foo' karaktersorozatot:

$ awk '/foo/ { print $2 }' BBS-list

-| 555-1234
-| 555-6699
-| 555-6480
-| 555-2127
A reguláris kifejezések alkalmasak kifejezések keresésére is. Ebben az esetben a keresett kifejezést adja meg a reguláris kifejezés; ugyanakkor amiben keresünk nem feltétlenül a teljes sor. Két operátor, a `~' és a `!~', használható a keresésnél. Ezekkel az operátorokkal leírt kifejezések használhatók mint minták az if, while, for és a do kifejezésekben.

exp ~ /regexp/
A kifejezés igaz, ha a regexp kifejezés illeszkedik az exp kifejezésre (mint karaktersorozatra). Az alábbi példa kikeres minden olyan sort, amelyben az első mező tartalmazza a nagy `J' betűt:

$ awk '$1 ~ /J/' inventory-shipped

-| Jan 13 25 15 115
-| Jun 31 42 75 492
-| Jul 24 34 67 436
-| Jan 21 36 64 620
Ez is ugyanazt csinálja:
awk '{ if ($1 ~ /J/) print }' inventory-shipped

exp !~ /regexp/
A kifejezés igaz, ha a regexp kifejezés nem illeszkedik az exp kifejezésre (mint karaktersorozatra). Az alábbi példa kiválaszt minden olyan sort, amelyben az első mező nem tartalmazza a nagy `J' betűt:

$ awk '$1 !~ /J/' inventory-shipped

-| Feb 15 32 24 226
-| Mar 15 24 34 228
-| Apr 31 52 63 420
-| May 16 34 29 208
...

Escape szekvenciák

Néhány karaktert nem lehet közvetlenül használni egy szöveg konstansban ("foo") vagy konstans reguláris kifejezésben (/foo/), mivel speciális jelentésük van. Ezeket a karaktereket úgynevezett escape szekvenciákkal írhatjuk le; a nevüket onnan kapták, hogy egy `\' (escape) karakterrel kezdődnek.

Az escape szekvenciák egyik használati lehetősége, hogy a macskaköröm karaktert egy szövegben elhelyezzük. Egy önmagában álló macskaköröm karakter a szöveg lezárását jelentené, ezért kell a `\"' karaktersorozatot használni a szövegen belül. Például:

$ awk 'BEGIN { print "He said \"hi!\" to her." }'
-| He said "hi!" to her.

A `\' karakter maga is egy speciális karakter és nem szerepelhet önállóan a szövegben; a `\\' karaktersorozatot kell használni egy szövegben vagy egy reguláris kifejezésben. Például azt a szöveget, amely egy macskakörmöt és egy `\' karaktert tartalmaz, így kell leírni: "\"\\".

A `\' karakter másik alkalmazási köre a kinyomtathatatlan karakterek reprezentációja, mint például a tab és az új sor karakter. Bár semmi nincs ami megakadályozhatna, hogy ezeket a karaktereket közvetlenül használd egy szövegben vagy egy reguláris kifejezésben, de lehet hogy az összhatás elég csúnya lenne a használatukkal.

Az alábbiakban az awk-ban használható escape szekvenciák egy táblázatát adjuk meg. Hacsak nem írjuk másképp, ezek az escape szekvenciák mind a szövegekben, mind a reguláris kifejezésekben érvényesek.

A gawk-ban van még további két escape szekvencia, aminek speciális jelentése van a reguláris kifejezésekben,

Mi történik ha egy szövegben olyan karakter elé teszel `\' karaktert amelyik nem szerepel a fenti táblázatban? A POSIX szabvány ezt az esetet tudatosan nem definiálja. Két lehetőség van:

Egy reguláris kifejezésben egy `\' karakter minden olyan karakter előtt, amely nem szerepel a fenti táblázatban vagy section Csak a gawk által értelmezett reguláris kifejezés operátorok alatt, azt jelenti, hogy a karaktert nem szabad értelmezni mint reguláris kifejezés operátort. Például a /a\+b/ a `a+b' három karakterre illeszkedik.

A teljes hordozhatóság érdekében a `\' karaktert csak akkor használd más karakterek előtt ha szükséges.

Érdekes kérdés lehet annak eldöntése is, hogy ha egy reguláris kifejezésekben használt operátort (metakaraktert) oktális vagy hexadecimális escape szekvenciával írunk le, akkor az awk mint egyszerű karakter vagy mint reguláris kifejezés operátor fogja azt kezelni.

Történelmileg, az ilyen karakterek mint egyszerű karakterek lesznek kezelve (s.s.). Ugyanakkor a POSIX szabvány azt írja elő, hogy mint reguláris kifejezés metakarakter kell őket kezelni; a gawk pontosan így tesz. A gawk "compatibility" módban viszont mint egyszerű karakter kezeli az így leírt escape szekvenciákat, például /a\52b/ megegyezik a /a\*b/ mintával.

Összefoglalva:

Reguláris kifejezés operátorok

Az egyszerű reguláris kifejezéseket az úgynevezett reguláris kifejezés operátorokkal vagy metakarakterekkel kombinálva összetettebb és sokoldalúbb mintákat lehet készíteni.

A fent bemutatott escape szekvenciák, section Escape szekvenciák, érvényesek a reguláris kifejezésekben is. A reguláris kifejezések feldolgozása során az awk először ezeket a karakter sorozatokat konvertálja az aktuális karakterré.

Itt közöljük a metakarakterek listáját. Minden olyan karakter, amely nem escape szekvencia és nem szerepel az alábbi listában egyszerűen önmagát jelenti.

A reguláris kifejezésekben a `*', `+', `?' operátoroknak és a kapcsos zárójeleknek van a legmagasabb precedenciájuk, majd ezt követi az összefűzés operátor. A legalacsonyabb precedenciája a `|' operátornak van. Mint a matematikában, a zárójelekkel megváltoztatható az operátorok kiértékelési sorrendje.

A karakterosztályok és intervallum operátorok használata nem engedélyezett ha a gawk "compatibility" módban fut.

A következő bekezdés tárgyalja a GNU specifikus reguláris kifejezés operátorokat, ill. további részleteket ad arról, hogy különböző parancssori opciók esetén a gawk hogyan értelmezi a karaktereket a reguláris kifejezésekben.

Csak a gawk által értelmezett reguláris kifejezés operátorok

A reguláris kifejezéseket használó GNU software-ek néhány extra operátort is ismernek, amelyeket ebben a bekezdésben tárgyalunk. Ezek az operátorok jelen esetben csak a gawk-ban találhatók meg, és más awk implementációkkal nem használhatók.

Ezen operátorok legtöbbje szavak illesztésére alkalmas. Jelen esetben egy szót betűk, számjegyek és a `_' karakter sorozataként definiálhatunk.

Más GNU software-ekben a szó határoló operátor a `\b', de ez összeütközésben van az awk nyelv definíciójával, ahol a `\b' a törlés (backspace) karakter, így a gawk egy másik operátort használ.

A különböző parancssori opciók befolyásolják a gawk interpretert, hogy hogyan értelmezze a reguláris kifejezésekben előforduló karaktereket:

Kis- és nagybetűk az illesztésekben

Lényeges, hogy a reguláris kifejezésekben kisbetűt vagy nagybetűt használunk a normál (nem meta-) karakterek illesztésénél. Így a `w' karakter egy reguláris kifejezésben csak a `w' kisbetűre fog illeszkedni a `W'-re nem.

A legegyszerűbb módja a kisbetűs, nagybetűs írásmódtól független illesztésnek a karakterlisták használata: `[Ww]'. Ugyanakkor ez elég kényelmetlen lehet ha sokat kell használni, ráadásul a reguláris kifejezéseket is elbonyolítja. Két lehetőség van a probléma megoldására.

Az első esetben, a program egy adott pontján az adatot átalakítjuk csak kisbetűs vagy csak nagybetűs írásmódba a tolower vagy a toupper beépített függvényekkel (amiket még eddig nem tárgyaltunk)

Például:

tolower($1) ~ /foo/ { ... }
az illesztés előtt az első mező minden karakterét átalakítja kisbetűvé. Ez bármely POSIX kompatíbilis awk implementációban működni fog.

A másik módszer csak gawk-ban használható, amikor is az IGNORECASE beépített változót át kell állítani egy nem zérus értékre. Amikor az IGNORECASE változó nem zérus, minden reguláris kifejezés és szöveg művelet esetén figyelmen kívül hagyja a kisbetű-nagybetű különbséget. A változó állítása a programon belül befolyásolja, hogy mely részeknél tegyen különbséget a kis- és nagybetűk között. Mivel kezdetben minden változó zérus értéket kap, így az IGNORECASE változó is, ezért tesz különbséget a kis- és nagybetűk között alapesetben.

x = "aB"
if (x ~ /ab/) ... # ez feltétel hamis
IGNORECASE = 1
if (x ~ /ab/) ... # most már sikeres lesz

Általában az IGNORECASE változó nem használható arra, hogy csak egyes szabályok esetén tegyen különbséget kis- és nagybetűk között és másoknál pedig nem, mivel nincs arra lehetőség hogy csak egy szabályra vagy mintára állítsuk be. A megoldást vagy a karakterlisták vagy a tolower beépített függvény adja. Az IGNORECASE változót lényegében az összes szabályra érdemes be- vagy kikapcsolni.

Az IGNORECASE változó beállítható a parancssorból vagy a BEGIN szabályban. Az IGNORECASE változó parancssorból történő beállítása esetén a program szerkesztése nélkül biztosítható, hogy a program nem fog különbséget tenni a kis- és nagybetűk között.

A gawk 3.0-ás verziója előtt az IGNORECASE változó értéke csak a reguláris kifejezésekkel végzett műveleteket érintette, és nem volt hatással a `==', `!=' és hozzájuk hasonló szövegösszehasonlító operátorokra. A 3.0-ás verziótól kezdve mind a reguláris kifejezésekkel, mind a szövegekkel végzett összehasonlítás esetén az IGNORECASE változó értéke hatással van a végeredményre.

A gawk 3.0-ás verziójától kezdve a kis- és nagybetűs karakterek azonossága az ISO-8859-1 (ISO Latin-1) karakterkészlet alapján dől el. Az eredeti 128 ASCII karakterkészlet csak egy részhalmaza ennek a karakterkészletnek, és az ISO-8859-1 segítségével több európai nyelv karakterei is használhatók.

Az IGNORECASE változó értéke nem számít ha a gawk "compatibility" módban fut. Ebben az esetben a kis- és nagybetűk közötti különbség mindig fontos.

Mennyi szöveg illeszkedik?

Vegyük a következő példát:

echo aaaabcd | awk '{ sub(/a+/, "<A>"); print }'

Ez a példa a sub függvényt használja, hogy a bemeneti soron módosítást végezzen. A /a+/ reguláris kifejezés "egy vagy több `a' karakterre illeszkedik" és amire lecserélnénk az az `<A>' szöveg.

A bemenet négy `a' karaktert tartalmaz. Mi lesz az eredmény? Más szavakkal, mennyi az "egy vagy több" -- az awk kettő, három vagy négy `a' karakterre fog illeszteni?

A válasz az, hogy az awk (és a POSIX is) mindig a lehető leghosszabb bemeneti karaktersorozatot fogja illeszteni. Így, ebben a példában, elsőre mind a négy karakterre illeszkedik a minta és egyszerre cseréli le a `<A>' kifejezésre.

$ echo aaaabcd | awk '{ sub(/a+/, "<A>"); print }'
-| <A>bcd

Egyszerű illeszkedik/nem illeszkedik teszteknél ez nem olyan fontos, de amikor regexp alapú mező és rekord feldarabolást kell csinálni és a match, sub, gsub és a gensub függvényeket kell használni, akkor ez nagyon fontossá válhat.

Dinamikus reguláris kifejezések használata

A `~' vagy `!~' operátorok esetén nem kötelező hogy a jobb oldalon egy reguláris kifejezés konstans álljon (valamilyen szöveg `/' jelek között), hanem bármilyen kifejezés állhat ott. Az awk a kifejezést kiértékeli és ha szükséges átalakítja szöveggé; a szöveg tartalmát használja, mint reguláris kifejezés. Az ilyen reguláris kifejezéseket dinamikus reguláris kifejezésnek hívják. Például:

BEGIN { identifier_regexp = "[A-Za-z_][A-Za-z_0-9]+" }
$0 ~ identifier_regexp { print }
beállítja a identifier_regexp változót egy olyan reguláris kifejezésre, ami az awk változók neveire illeszkedik, majd megpróbálja illeszteni a bemeneti adatokra.

Figyelem: Amikor a `~' és a `!~' operátorokat használod jelentős különbség van a `/' jelek között megadott regexp konstansok és a macskakörmök között megadott szöveg konstansok között. Amikor a szöveg konstanst használod, tudnod kell, hogy a szöveg kétszer lesz feldolgozva; egyszer amikor az awk beolvassa a programodat, másodszor amikor a bal oldalon levő szöveget megpróbálja illeszteni a jobb oldali mintához. Ez igaz minden szöveg tartalmú kifejezésre (így a fenti identifier_regexp változóra is) és nem csak a szöveg konstansokra.

Miért fontos, hogy a szöveg kétszer lesz feldolgozva ? A válasz az escape szekvenciákat érinti és különösen a `\' karaktert. Ahhoz, hogy a `\' karaktert egy reguláris kifejezésben használhassuk mint egyszerű karaktert, a `\' karaktert kétszer kell leírni.

Például a /\*/ kifejezés egy olyan regexp konstans ami megfelel az egyszerű `*' karakternek. Csak egy `\' karakter kell! Ugyanezt megcsinálni egy szöveg konstans esetén így lehet, "\\*". Az első `\' védi a másodikat és valójában csak két karakter van a szövegben a `\' és a `*' karakterek.

Tehát ha regexp és szöveg konstansok is használhatók reguláris kifejezések leírására, melyiket használjuk? A "regexp konstansokat", több okból is.

  1. A szöveg konstansokat bonyolultabb írni és nehezebb olvasni is. Ha regexp konstansokat használsz kevesebbet hibázhatsz. Gyakori hiba, hogy az emberek nem értik a két konstans közötti különbséget.
  2. A regexp konstansok használata hatékonyabb is: az awk a reguláris kifejezéseket egy belső struktúrában tárolja, így a mintaillesztés gyorsabb. Amikor szöveg konstanst használsz, az awk-nak először át kell alakítania a kifejezést ebbe a belső struktúrába, és csak utána tud mintaillesztést csinálni.
  3. A regexp-ek használata egy szebb programozási stílus; világosan mutatja, hogy egy reguláris kifejezés illesztést akartál csinálni

Kifejezések

A kifejezések az awk minták és tevékenységek alap építőkövei. Egy kifejezés kiértékelése egy értéket ad, amit kinyomtathatsz, tesztelhetsz, egy változóban eltárolhatsz vagy egy függvénynek átadhatsz mint argumentumot. Továbbá egy kifejezéssel új értéket rendelhetsz egy változóhoz vagy mezőhöz az értékadó operátorral.

Egy kifejezés önmagában szolgálhat mint egy minta vagy egy tevékenység. A legtöbb kifejezés olyan más kifejezéseket tartalmaz, amelyek adatokon dolgoznak. Mint más nyelvekben, az awk-ban is egy kifejezés tartalmazhat változót, tömb elemre hivatkozást, konstans elemet, függvényhívást és ezek bármilyen kombinációját különböző operátorral.

Konstans kifejezések

A legegyszerűbb kifejezés egy konstans, aminek mindig ugyanaz az értéke. Háromféle konstans van: számkonstans, szövegkonstans és reguláris kifejezés konstans.

Szám- és szövegkonstansok

Egy számkonstans értéke maga a szám. A szám lehet egész, lebegőpontos vagy exponenciális alakú valós szám. Alább bemutatunk néhány számkonstanst; mindegyiknek ugyanaz az értéke:

105 1.05e+2 1050e-1

A szövegkonstans karakterek sorozatából áll és macskakörmök veszik körül, például:

"parrot"

Ez egy olyan szöveget reprezentál, aminek a tartalma: `parrot'. A gawk-ban a szövegek bármilyen hosszúak lehetnek, és bármely 8 bittel leírható ASCII karaktert tartalmazhatják, az ASCII NUL-t is. Más awk implementációknak néhány speciális karakter problémát okozhat.

Reguláris kifejezés konstansok

Egy reguláris kifejezés konstans egyszerűen a `/' karakterek között leírt reguláris kifejezés, mint a /^beginning and end$/. Leggyakrabban reguláris kifejezés konstansokat használunk, de a `~' és a `!~' operátorokkal "dinamikus" reguláris kifejezéseket is lehet használni (amik egy reguláris kifejezést tartalmazó egyszerű szövegek vagy változók).

Reguláris kifejezés konstansok használata

Ha a reguláris kifejezés konstans a `~' vagy a `!~' operátor jobb oldalán áll, akkor magát a reguláris kifejezést jelenti, amit illeszteni szeretnénk.

A reguláris kifejezés konstansok (mint a /foo/) használhatók mint egyszerű kifejezések is. Ha a reguláris kifejezés konstans önmagában áll, az megegyezik azzal az esettel, mintha a mintában lett volna megadva, például: `($0 ~ /foo/)' (s.s.). Ez azt jelenti, hogy az alábbi két programrészlet

if ($0 ~ /barfly/ || $0 ~ /camelot/)
print "found"
és
if (/barfly/ || /camelot/)
print "found"
teljesen megegyezik.

Ennek a szabálynak az a furcsa következménye, hogy bár az alábbi kifejezés nem hibás, de nem azt csinálja, mint amit valószínűleg elvárnánk:

# figyelem: a /foo/ nem a ~ operátor bal oldalán van if (/foo/ ~ $1) print "found foo"
Elméletileg a $1 mezőre a /foo/ reguláris kifejezést próbálja illeszteni. Valójában a `/foo/ ~ $1' kifejezés ezzel egyezik meg: `($0 ~ /foo/) ~ $1'. Más szavakkal, először a /foo/ reguláris kifejezést illeszti a teljes rekordra, aminek az eredménye egy vagy zérus attól függően, hogy az illesztés sikerül-e vagy sem. Azután ezt az eredményt próbálja meg illeszteni az első mezőre.

Mivel valószínű, hogy ilyen tesztet soha nem akarsz elvégezni, ezért a gawk figyelmeztet ha ilyen szerkezetet talál a programban.

Egy másik következménye a fenti szabálynak, hogy az alábbi értékadás

matches = /foo/
vagy zérust vagy egyet tárol a matches változóban, attól függően, hogy mi az aktuális bemeneti rekord értéke.

Ez az awk tulajdonság soha nem volt megfelelően dokumentálva a POSIX szabvány előtt.

Reguláris kifejezés konstansok használhatók a gensub, sub és gsub függvények első argumentumaként és a match függvény második. Az awk modern implementációi és a gawk megengedi, hogy a split függvény harmadik argumentuma reguláris kifejezés konstans legyen. Régebbi awk implementációkban ez nem megengedett (s.s.).

Sajnos ez kavarodást okozhat a felhasználó által definiált függvények esetén, ha egy reguláris kifejezés konstanst adunk meg mint argumentum, például:

function mysub(pat, repl, str, global)
{
   if (global)
     gsub(pat, repl, str)
   else
     sub(pat, repl, str)
   return str
}

{
   ...
   text = "hi! hi yourself!"
   mysub(/hi/, "howdy", text, 1)
   ...
}
A példában egy reguláris kifejezés konstanst szeretnénk átadni a mysub függvénynek, ami továbbadja azt vagy a sub vagy a gsub függvénynek. Valójában a pat paraméter zérus vagy egy attól függően, hogy a rekord ($0) tartalmazza-e a /hi/ szöveget.

Mivel nem valószínű, hogy az illesztés eredményét szeretnéd átadni mint argumentum, ezért a gawk figyelmeztet ha egy reguláris kifejezés konstanst talál egy a felhasználó által definiált függvény argumentum listájában.

Értékadás

változó=text
Ilyen formában beállítható egy változó értéke az awk futtatása kezdetén. Az értékadás a bemeneti file-ok között is elhelyezhető.

Ha az értékadás előtt a `-v' opciót használjuk, például így:

-v változó=text
akkor a változót állítja be legelőször, még a BEGIN szabály lefutása előtt. A `-v' opciónak és az értékadásnak meg kell előznie az összes bemeneti file-t és a program szövegét is.
Ellenkező esetben az értékadás csak akkor történik meg, amikor az awk a feldolgozásban odaér, vagyis miután feldolgozta a megelőző bemeneti file-t. Például:
awk '{ print $n }' n=4 inventory-shipped n=2 BBS-list
kinyomtatja az n-edik mezőt mindegyik bemeneti rekordból. Mielőtt az első file-t elkezdené olvasni beállítja az n változó értékét négyre. Ennek hatására az `inventory-shipped' file-ból a negyedik mezőt fogja kinyomtatni. Miután befejezte az első file feldolgozását és mielőtt elkezdené feldolgozni a másodikat az n változót kettőre állítja, így a `BBS-list' file-ból a második mezőt nyomtatja ki.
$ awk '{ print $n }' n=4 inventory-shipped n=2 BBS-list
-| 15
-| 24
...
-| 555-5553
-| 555-3412
...
A parancssori argumentumokat explicit módon is meg lehet vizsgálni egy awk programban, mivel az ARGV tömbben rendelkezésre állnak.

Az awk a parancssori értékadásnál is figyelembe veszi az escape szekvenciákat (s.s.)

Változódeklaráció

A változókban olyan értéket tárolhatunk, amelyet a programban később szeretnénk felhasználni. A változókat teljesen szabadon lehet a programon belül manipulálni. Az awk parancssorában kezdőértéket adhatunk meg a változóknak.

Változók használata egy programban

A változókkal nevet adhatunk egy értéknek, amire később a név segítségével hivatkozhatunk. Már több példában használtunk változókat. A változó neve betűket, számokat és aláhúzás karaktert tartalmazhat, de nem kezdődhet számjeggyel. A kis- és nagybetűs írásmód fontos, mivel az a és az A két, független változót jelöl.

Önmagában egy változó neve egy érvényes kifejezés; a változó jelenlegi értékét reprezentálja. A változóknak új értéket adhatunk az értékadó operátorral vagy megváltoztathatjuk a növelő vagy csökkentő operátorral.

Néhány változónak speciális, beépített jelentése van; például FS a mezőelválasztót és az NF a mezők számát adja meg. A section Beépített változók, tartalmazza a beépített változók listáját. Ezeket a beépített változókat ugyanúgy használhatjuk mint más változókat, de az awk is megváltoztathatja az értéküket. Minden beépített változó neve csupa nagybetűből áll.

Az awk változók értéke szám vagy szöveg lehet. Alapesetben minden változó kezdőértéke az üres szöveg, ami zérusnak felel meg ha számmá konvertáljuk. Ezért nincs szükség a változók "inicializálására" az awk-ban, mint például a C programozási nyelvben.

Üres utasítás

Az awk-ban nem megengedett az üres utasítás használata

Összetett utasítás

Szekvencia

Szekvenciát többféleképpen is meg tudunk valósítani: vagy pontosvesszővel választjuk el egymástól az utasításokat, vagy sorvégejellel, azaz külön sorba írjuk a következő utasítást. Pl.:

{ print $n ; print $0 }

Blokkutasítás

A blokkutasításokat kapcsoszárojelek között definiálhatjuk. Pl.:

BEGIN {@
   print $0
}

END {
   print $n
}

$0 > 0 {
   print $0
}

Feltétel nélküli vezérlésátadás

Feltétel nélküli vezérlésátadás nincsen az AWK-ban.

Elágazás

Egyirányú

Kétféleképpen valósíthatunk meg elágazást:

Elágazás IF kulcsszóval

Logikai kifejezések összehasonlítása

Sok programozási nyelvben speciális reprezentációja van az "igaz" és a "hamis" értékeknek. Ezek a nyelvek általában speciális konstanst használnak, például true és false vagy TRUE és FALSE.

Az awk ettől különböző, ugyanazt az egyszerű megoldást használja mint a C programozási nyelv. Az awk-ban, bármely nem zérus szám vagy nem üres szöveg igaz értéket képvisel. A zérus szám és az üres szöveg "" hamis. Az alábbi program háromszor írja ki a `Egy furcsa igaz érték' szöveget:

BEGIN {
   if (3.1415927)
     print "Egy furcsa igaz érték"
   if ("Four Score And Seven Years Ago")
     print "Egy furcsa igaz érték"
   if (j = 57)
     print "Egy furcsa igaz érték"
}
A "nem zérus vagy nem üres szöveg" szabálynak van egy érdekes következménye: A "0" szöveg konstans valójában igaz, mivel nem üres szöveg (s.s.).

Változó típusok összehasonlítása

Más programozási nyelvekkel szemben az awk változóknak nincs fix típusuk, lehetnek számok vagy szövegek, attól függően, hogy mi az értékük.

Az 1992-es POSIX szabvány bevezette a szám-szöveg (strnum) koncepciót; ez egyszerűen egy szöveg ami úgy néz ki mint egy szám, például " +2". E koncepció segítségével lehet meghatározni a változó típusát.

A változó típusa azért fontos, mert a típus határozza meg, hogy két változó hogyan lesz összehasonlítva.

A gawk-ban az alábbi szabályok érvényesek.

  1. Egy számnak vagy egy matematikai műveletnek szám attribútuma van.
  2. Egy szövegnek vagy egy szöveges műveletnek szöveg attribútuma van.
  3. Mezőknek, a getline input-nak, a FILENAME-nek, az ARGV elemeinek, az ENVIRON elemeinek és a split által készített tömbök olyan elemeinek aminek szám-szöveg értéke van az attribútuma strnum. Minden egyéb esetben az attribútum szöveg. Nem inicializált változók attribútuma is strnum.
  4. Az attribútum átadódik az értékadással, de a változó egyszerű használatával nem változik meg.
Az utolsó szabály különösen fontos. A következő programban a-nak szám értéke van, még akkor is ha később egy szöveges műveletben használjuk.
BEGIN {
   a = 12.345
   b = a " is a cute number"
   print b
}
Amikor két operandust hasonlítunk össze vagy szöveges vagy szám összehasonlítás hajtódik végre, az operandusok típusától függően, az alábbiak szerint:

Az alapkoncepció az, hogy a felhasználó által megadott bemenetet ami számnak néz ki, és csak a felhasználói bemenetet, számként kell kezelni még akkor is ha karakterekből áll, és ezért szöveg lenne.

Az összehasonlító kifejezések a szövegek és a számok közötti kapcsolatot ellenőrzik, például egyenlőségüket. Az összehasonlító kifejezéseket egy összehasonlító operátorral írjuk le, amelyek a C nyelvben található operátorokkal felülről kompatíbilisek. Íme az összehasonlító operátorok listája:

Az összehasonlító kifejezések értéke egy ha igaz, és zérus ha hamis.

Amikor különböző típusú komponenseket hasonlítunk össze, akkor a szám értékeket átalakítja szöveggé a CONVFMT változó értékét használva.

A szövegek összehasonlításánál először az első karaktereket hasonlítja össze, majd a második karaktereket és így tovább. Így "10" kisebb mint "9". Két olyan szöveg esetén, amikor az egyik szöveg eleje teljesen megegyezik a második szöveggel, akkor a rövidebb szöveg számít kisebbnek. Így "abc" kisebb mint "abcd".

Nagyon könnyü véletlenül elgépelni a `==' operátort és az egyik (`=') egyenlőség jelet elhagyni. Az eredmény szintén érvényes awk kód, de a program nem azt fogja csinálni mint amit szeretnél.

if (a = b) # hoppá ! a == b kellene
...
else
...
Hacsak b nem zérus vagy üres szöveg, az if feltételes kifejezés mindig igaz lesz. Az ilyen hibát sajnos nagyon nehéz észrevenni a forráskód átnézése során.

Alább bemutatunk néhány kifejezést, ami bemutatja, hogy a gawk hogyan végzi az összehasonlítást és milyen eredményt kapunk:

Ebben a példában,
$ echo 1e2 3 | awk '{ print ($1 < $2) ? "true" : "false" }'
-| false
az eredmény hamis, mivel $1 és $2 szám-szövegek, így mindkettő típusa strnum, ami szám összehasonlítást eredményez.

Az összehasonlítási szabályok és a szám-szövegek célja, hogy a program a "lehető legkisebb meglepetést" okozva a felhasználó által "elvárt, jó dolgot csinálja".

A szöveg és reguláris kifejezések összehasonlítása teljesen különböző. Például:

x == "foo"
értéke egy, vagyis igaz, ha az x változó értéke pontosan `foo'. Ezzel ellentétben, az
x ~ /foo/
értéke egy, ha az x változó tartalmazza a `foo' szöveget, úgy mint "Oh, what a fool am I!".

A `~' és `!~' operátorok jobb oldalán álló kifejezés lehet egy regexp konstans (/.../) vagy egy általános kifejezés amikor is a kifejezés értékét mint szöveget használja egy dinamikus reguláris.

Az awk jelenlegi implementációjában egy konstans reguláris kifejezés a `/' jelek között szintén általános kifejezésnek számít. A /regexp/ csak egy rövidítése az összehasonlító kifejezésnek:

Feltételes kifejezések

A feltételes kifejezések olyan speciális kifejezések aminek három operandusa van. Ennek segítségével egy kifejezés értékétől függően az egyik vagy egy másik kifejezés hajtódik végre.

A feltételes kifejezés ugyanolyan mint a C nyelvben:

választás ? ha-igaz-kif : ha-hamis-kif
Három alkifejezésből áll. Az első, a választás, értékelődik ki legelőször. Ha az értéke "igaz" (nem zérus és nem üres szöveg) akkor a ha-igaz-kif kifejezés lesz kiértékelve és ennek az értéke lesz a teljes kifejezés értéke. Más esetben a ha-hamis-kif lesz kiértékelve és ennek az értéke adja a teljes kifejezés értékét.

Például, ez a kifejezés az x változó abszolút értékét adja meg:

x > 0 ? x : -x
Minden alkalommal, amikor egy feltételes kifejezés kiértékelődik, pontosan egy kifejezés, vagy a ha-igaz-kif vagy a ha-hamis-kif értékelődik ki; a másikat nem veszi figyelembe. Ez akkor fontos, ha a kifejezéseknek mellékhatásuk van. Például ez a feltételes kifejezés vagy az a vagy a b tömb i-edik indexét vizsgálja meg és az i-t megnöveli.
x == y ? a[i++] : b[i++]
Ez a kifejezés garantáltan csak egyszer növeli meg a i értékét, mivel minden alkalommal a két növelő kifejezés közül csak az egyik hajtódik végre, és a másik nem.

Egy apró gawk módosítás, hogy egy új sor karakter beszúrható a `?:' karakterek bármelyike után, és így több sorba is írható a kifejezés. Ugyanakkor új sor karakter nem használható előttük kivéve ha a `\' karaktert Ha a `--posix' opció meg van adva, akkor ez a kiegészítés nem használható.

Többirányú

Többirányú elágazást nincsen az AWK-ban. Tehát a többirányú elágazási szerkezetet, ha mégis ilyenre lenne szükségünk, a kétirányú elágazással kell megoldanunk.

Ciklusok

Nem ismert lépésszámú

Elöltesztelő

A while kifejezés a legegyszerűbb hurokképző kifejezés az awk-ban. A while addig hajt végre más kifejezéseket, amíg a feltétel igaz. A kifejezés formája:

while (feltétel)
   test
A test tartalmazza a végrehajtandó kifejezéseket, és a feltétel az a kifejezés, ami szabályozza, hogy a hurok hányszor fusson le.

A while kifejezés először kiértékeli a feltétel kifejezést. Ha a feltétel igaz, akkor végrehajtja a test kifejezést. Miután a test végrehajtódott, a feltételt újra kiértékeli, és ha még mindig igaz, akkor a test ismét lefut. Ezt az eljárást addig ismétli, amíg a feltétel hamis nem lesz. Ha a feltétel kezdetben hamis, akkor a hurok teste soha nem hajtódik végre, és az awk a hurok utáni kifejezéssel folytatja a program végrehajtását.

Ez a példa minden rekord első három mezőjét nyomtatja ki, egyet egy sorba.

awk '{ i = 1
   while (i <= 3) {
     print $i
     i++
   }
}' inventory-shipped
Itt a hurok teste egy összetett kifejezés kapcsos zárójelek között, ami két kifejezésből áll.

A hurok valahogy így működik: először az i értéke egy lesz. Ezután a while ellenőrzi, hogy az i kisebb-e mint három vagy egyenlő-e hárommal. Ez igaz, hiszen az i értéke egy, így kinyomtatja az i-edik mezőt. Ezek után a `i++' kifejezés megnöveli az i értékét, majd a hurok megismétli ezt a folyamatot. A hurok véget ér amikor az i értéke négy lesz.

Mint látható, új sor nem szükséges a feltétel és kifejezés teste között, de a kifejezés több sorba szedése jobban olvashatóvá teszi a programot. Kivéve persze ha összetett kifejezést használunk vagy nagyon egyszerű kifejezést. Az új sor a nyitó zárójel után, ami az összetett kifejezést elkezdi szintén nem szükséges, de akkor a programot nehezebb olvasni.

Hátultesztelő

do
   test
while (feltétel)
Még ha a feltétel kezdetben hamis is, a test legalább egyszer végrehajtódik (és csak egyszer, hacsak a test igazzá nem teszi a feltételt. Érdemes ezt összehasonlítani az alábbi while kifejezéssel:
while (feltétel)
   test
Ez a kifejezés nem hajtja végre a test kifejezést egyszer sem, ha a feltétel hamis kezdetben.

Itt egy példa a do kifejezésre:

awk '{ i = 1
   do {
     print $0
     i++
   } while (i <= 10)
}'
Ez a program minden rekordot tízszer nyomtat ki. Ez nem egy valós példa, mivel egy közönséges while hurok is megtenné. Ugyanakkor egy tapasztalatot is tükröz, hogy nagyon ritkán van valós indok a do kifejezés használatára.

Ismert lépésszámú

A for kifejezés kényelmessé teszi iterációs ciklusok készítését. A for kifejezés általános formája:

for (inicializálás; feltétel; növelés)
   test
Az inicializálás, a feltétel és a növelés tetszőleges awk kifejezések és a test az ismételten végrehajtandó awk kifejezés.

A for kifejezés először az inicializálás kifejezést hajtja végre. Ezután, amíg a feltétel igaz, ismételten végrehajtja a testet majd az növelés kifejezést. Tipikusan az inicializálás a változót egyre vagy zérusra állítja, a növelés eggyel növeli azt és a feltétel ellenőrzi, hogy az iterációk száma elérte-e a kívánt értéket.

Itt egy példa a for kifejezésre:

awk '{ for (i = 1; i <= 3; i++)
   print $i
}' inventory-shipped
A program minden bemeneti rekord első három mezőjét kinyomtatja, egy mezőt soronként.

Egynél több változót nem lehet beállítani az inicializálás részben kivéve a többszörös értékadás, mint `x = y = 0', ami csak akkor használható ha minden változó kezdeti értéke egyenlő. (Persze a pótlólagos változóknak külön-külön is értéket adhatunk a for ciklus előtt.)

Ugyanez igaz a növelés részre is; ahhoz, hogy egynél több változót növeljünk meg, a ciklus végén kell ezt elvégezni külön kifejezésekkel. A C programozási nyelvben az összetett kifejezések kialakítására használható vessző operátor az ilyen esetekben hasznos lehet, de az awk-ban nem támogatott.

Leggyakrabban a növelés egy növelő kifejezés, mint a fenti példában. De ez nem kötelező; ez bármilyen kifejezés lehet. Például, ez a kifejezés kinyomtatja a kettes szám egy és száz közé eső hatványait:

for (i = 1; i <= 100; i *= 2)
   print i
A for utáni zárójelek közé eső három paraméter közül bármelyik elhagyható, ha abban a pozícióban nincs mit csinálni. Így a `for (; x > 0;)' kifejezés egyenértékű a `while (x > 0)' kifejezéssel. Ha a feltétel nincs megadva, akkor a feltétel mindig true, vagyis igaz, ami egy végtelen ciklust eredményez (pl. a ciklus soha nem ér véget).

A legtöbb esetben, egy for ciklus egy while hurok rövidítése, ahogy ezt alább bemutatjuk:

inicializálás
while (feltétel) {
   test
   növelés
}
Az egyetlen kivétel, amikor a continue kifejezés szerepel a ciklusban. Ebben az esetben ha a for kifejezést átalakítjuk while kifejezésre, akkor a continue kifejezés hatását is könnyen megváltoztathatjuk akaratlanul.

A for ciklusnak van egy alternatív formája, ami egy tömb elemein megy végig:

for (i in tömb)
   csinálj valamit az elemmel array[i]
Az awk nyelv a while hurokképző kifejezésen kívül a for kifejezéssel is rendelkezik, mivel egy for ciklus begépelése kevesebb időt vesz igénybe, és egy természetesebb gondolkodást támogat. Az iterációs lépések számolása egy természetes feladat a ciklusokban. Egyszerűbb erről úgy gondolkodni, hogy ez a számolás a ciklus része, mint hogy a cikluson belül kelljen ezt elvégezni.

Végtelen ciklusok

Végtelen ciklust elöl- vagy hátultesztelő ciklussal tudunk írni úgy, hogy olyan terminálási feltételt adunk meg, amit sohasem válik igazzá.

Vezérlésátadó utasítások

A break kifejezés

A break kifejezés a legbelsőbb for, while vagy do hurokból lép ki. A következő példa egy egész szám legkisebb osztóját keresi meg, és azonosítja a prímszámokat is:

awk '# legkisebb osztó keresése
   { num = $1
   for (div = 2; div*div <= num; div++)
     if (num % div == 0)
       break
   if (num % div == 0)
     printf "A %d legkisebb osztója a %d\n", num, div
   else
     printf "%d prím\n", num
   }'
Ha a maradék zérus az első if kifejezésben, az awk azonnal kilép a for ciklusból. Ez azt jelenti, hogy az awk közvetlenül a ciklus utáni kifejezéssel folytatódik.

Itt van még egy program, ami teljesen azonos az előzővel és bemutatja, hogy hogyan lehet a for vagy a while kifejezés feltétel részét lecserélni egy if és egy break kifejezéssel:

awk '# find smallest divisor of num
   { num = $1
   for (div = 2; ; div++) {
     if (num % div == 0) {
       printf "Smallest divisor of %d is %d\n", num, div
       break
     }
     if (div*div > num) {
       printf "%d is prime\n", num
       break
     }
   }
}'
Mint azt már korábban elmondtuk, a break kifejezésnek nincs semmi jelentése egy ciklus testén kívül. Ugyanakkor, bár ez soha nem volt dokumentálva, az awk régebbi implementációi a break kifejezést egy cikluson kívül úgy kezelték mint egy next kifejezés. Az awk jelenlegi implementációi többé nem támogatják ezt a viselkedést. A gawk támogatja a break ilyen viselkedését ha a parancssorban a `--traditional' opció is meg van adva. Más esetekben hibát eredményez, mivel a POSIX szabvány a break kifejezést úgy definiálja, hogy csak cikluson belül használható (s.s.).

A continue kifejezés

A continue kifejezés, mint a break kifejezés csak a for, a while és a do cikluson belül használható. A continue kifejezés végrehajtása a ciklus további részét átugorja, aminek hatására az újabb ciklus elkezdődik. Ezzel ellentétben a break kilép a ciklusból.

Egy for ciklusbeli continue kifejezés arra utasítja az awk-ot, hogy ugorja át a ciklus további részét és a for kifejezés növelő kifejezés részével folytassa a futtatást. Az alábbi program ezt a tényt illusztrálja:

awk 'BEGIN {
   for (x = 0; x <= 20; x++) {
     if (x == 5)
       continue
     printf "%d ", x
   }
   print ""
}'

Ez a program kinyomtatja a számokat zérustól 20-ig, kivéve az ötös számot, amikor a printf kifejezést átugorja. Mivel a növelő `x++' kifejezést nem ugorja át, az x változónak nem öt lesz az értéke ezek után. A fenti for ciklust érdemes összehasonlítani az alábbi while ciklussal:

awk 'BEGIN {
   x = 0
   while (x <= 20) {
     if (x == 5)
       continue
     printf "%d ", x
     x++
   }
   print ""
}'
Ez a program végtelen ciklusba kerül miután az x változó öt értéket kap.

Mint azt már korábban elmagyaráztuk, a continue kifejezésnek nincs értelme egy cikluson kívül. Ugyanakkor, bár eddig nem volt dokumentálva, az awk régi implementációjában a continue kifejezés cikluson kívüli használata a next kifejezésnek felelt meg. A Unix awk jelenlegi implementációi nem támogatják ezt a viselkedést. A gawk lehetővé teszi ezt viselkedést ha a `--traditional' opció szerepel a parancssorban. Minden más esetben ez hibát jelent, mivel a POSIX szabvány a continue ilyen viselkedését nem definiálja (s.s.).

A next kifejezés

A next kifejezés arra utasítja az awk-ot, hogy azonnal fejezze be a jelenlegi rekord feldolgozását és folytassa a következő rekorddal. A jelenlegi szabály további tevékenység részét sem hajtja végre.

Érdemes ezt összehasonlítani a getline függvény hatásával. A getline kifejezés hatására is az awk azonnal beolvassa a következő rekordot, de nem változtatja meg a program futási folyamatát semmilyen módon. Így az aktuális tevékenység az új rekorddal folytatja a feldolgozást.

A legmagasabb szinten egy awk program végrehajtása egy olyan ciklus, ami beolvassa a bemeneti rekordokat és minden szabállyal szemben teszteli is azokat. Ha erre a ciklusra úgy gondolsz, mint egy for kifejezésre, aminek a testét a szabályok alkotják, akkor a next kifejezés megfelel a continue kifejezésnek: átugorja a ciklus testének végét és a növelés kifejezést hajtja végre (ami ebben az esetben beolvassa a következő rekordot).

Például, ha az awk programod csak négy mezőből álló rekordokon dolgozik és nem akarod, hogy rossz bemenet esetén felmondja a szolgálatot akkor egy ilyen szabályt használhatsz a program elején:

NF != 4 {
   err = sprintf("%s:%d: skipped: NF != 4\n", FILENAME, FNR)
   print err > "/dev/stderr"
   next
}
így a következő szabályok nem kapják meg a rossz rekordot. A hibaüzenet át van irányítva a szabványos hibakimenetre, mint ahogy minden hiba esetén lennie kell.

A POSIX szabvány szerint, a next kifejezés viselkedése nem definiált a BEGIN és az END szabályokban. A gawk szintaktikai hibát fog jelezni. Bár a POSIX szabvány megengedi, de néhány awk implementáció nem engedi meg a next kifejezés használatát egy függvényben. Mint minden más next kifejezés, a függvény belsejében elhelyezett next kifejezés is beolvassa a következő rekordot és elkezdi feldolgozni a program első szabályával.

Ha a next kifejezés használatával eléri a bemenet végét, akkor az END szabályon belüli kódot hajtja végre. See section A BEGIN és az END speciális minták.

Figyelem: Néhány awk implementáció futási időben hibát generál, ha egy a felhasználó által definiált függvényen belül a next kifejezést használjuk. A gawk-nak nincs ilyen problémája.

Az exit kifejezés

Az exit kifejezés hatására az awk azonnal befejezi az aktuális szabály végrehajtását, és abbahagyja a bemenet feldolgozását is; minden további bemenetet figyelmen kívül hagy. A formája az alábbi:

Ha egy exit kifejezést hajt végre az awk a BEGIN szabályban akkor azonnal befejez minden működést. Semmilyen bemenetet nem olvas be. Ugyanakkor ha egy END szabály is van a programban, akkor azt is végrehajtja.

Ha egy exit kifejezés hajtódik végre az END szabályban, akkor a program azonnal leáll.

Egy exit kifejezés ami nem része sem egy BEGIN sem egy END szabálynak azonnal befejezi a további szabályok végrehajtását az adott rekordra, átugorja a további bemeneti rekordokat és végrehajtja az END szabályt, ha van ilyen.

Ha azt akarod, hogy az END szabály ne fusson le ebben az esetben, akkor egy változót be kell állítani egy nem zérus értékre az exit kifejezés előtt, és ezt a változót ellenőrizni kell az END szabályon belül.

Ha egy argumentumot is megadunk az exit kifejezésnek, akkor ez az érték lesz az awk kimeneti státusz kódja, visszatérési értéke. Ha nincs argumentum megadva, akkor az exit státusza zérus lesz (siker). Abban az esetben amikor először az exit kifejezésnek egy argumentumot adunk meg, majd a második esetben nem argumentummal hívjuk meg, akkor az előző kimeneti értéket fogja használni (s.s.).

Például, tegyük fel, hogy egy olyan esetet fedezel fel, amit nem tudsz hogyan kellene kezelni. Hagyományosan a program ezt úgy jelenti neked, hogy nem zérus státusszal lép ki. Az awk programod is megteheti ezt, ha az exit kifejezést egy nem zérus argumentummal használod. Íme egy példa erre:

BEGIN {
   if (("date" | getline date_now) <= 0) {
     print "Can't get system date" > "/dev/stderr"
     exit 1
   }
   print "current date is", date_now
   close("date")
}