Az AWK programozási nyelv

Absztrakció

Függvényabsztrakció

Bonyolult awk programokat egyszerűsíteni lehet, ha saját függvényeket definiálsz. Az általad definiált függvényeket ugyanúgy kell meghívni, mint a beépített függvényeket, de neked kell definiálni a függvényeket -- megmondani az awk-nak, hogy a függvénynek mit kell csinálnia.

A függvénydefinició formája

A függvények definíciója bárhol előfordulhat az awk program szabályai között. Így egy awk program általános formája valójában szabályok és függvénydefiníciók sorozata. Nincs szükség arra, hogy a függvények definícióját a használatuk előtt adjuk meg, mivel az awk először beolvassa az egész programot, és csak utána kezdi el végrehajtani.

Egy name nevű függvény definíciója így néz ki:

function name(parameter-list)
{
  body-of-function
}

Egy érvényes függvény neve ugyanolyan, mint egy érvényes változó neve: betűk, számok és az aláhúzás karakterek sorozata, amelyek nem kezdődhetnek számmal. Egy awk programon belül egy adott név vagy változó, vagy tömb, vagy függvény.

A parameter-list a függvény argumentumainak és a lokális változóknak a listája, vesszővel elválasztva az elemeit. Amikor egy függvényt meghívunk, akkor az argumentumban adott nevek fogják a hívás során megadott értékeket tartalmazni. A lokális változók kezdeti értéke az üres szöveg. Egy függvénynek nem lehet két azonos nevű paramétere.

A body-of-function awk kifejezéseket tartalmaz. Ez a függvény legfontosabb része, mivel ez adja meg, hogy a függvénynek mit is kell csinálnia. Az argumentumok egy bizonyos fajta kommunikációt biztosítanak a program többi részével; a lokális változók ideiglenes tárolási helyet biztosítanak.

Az argumentumok és a lokális változók nem különböztethetők meg szintaktikailag; ehelyett a hívás során megadott argumentumok száma határozza meg, hogy mennyi argumentuma lesz a függvénynek. Így, ha három argumentummal hívjuk meg a függvényt, akkor az első három név a parameter-list listában argumentum lesz, a többi lokális változó.

Ebből következik, hogy ha nem mindig ugyanannyi argumentummal hívjuk meg a függvényt, akkor egyes nevek a parameter-list listában néha argumentumok lesznek, néha lokális változók. Egy másik felfogás szerint, a meg nem adott argumentumok kezdeti értéke az üres szöveg lesz.

Általában amikor a függvényt írod, akkor már tudod, hogy mennyi argumentumot akarsz használni és mennyi lokális változót, így az a szokás, hogy néhány extra szóköz karaktert kell az argumentumok és a lokális változók közé tenni, hogy ezzel dokumentáld a funkcióbeli különbséget.

A függvény futtatása során az argumentumok és lokális változók "eltakarják" a programod más részein használt azonos nevű változókat. Az "eltakart" változók nem elérhetőek a függvényben, mivel a nevük az adott függvényben valami mást jelentenek. Minden más változót, amit az awk programban használtál el lehet érni és megváltoztatni a függvényen belül.

Az argumentumok csak a függvény belsejében érvényesek. Ha a függvény befejezte a működését és kilépett, akkor újra el tudod érni a függvény által "eltakart" változókat.

A függvény maga is tartalmazhat olyan kifejezéseket, amelyek másik függvényt hívnak meg. A függvény még saját magát is meghívhatja, közvetlenül vagy indirekt módon. Ekkor a függvényt rekurzívnak hívjuk.

A legtöbb awk implementációban, a gawk-ban is, a function kulcsszót rövidíteni lehet a func szóval. Ugyanakkor a POSIX szabvány csak a function kulcsszót definiálja. Ennek az a praktikus következménye, hogy ha a gawk POSIX kompatíbilis módban, akkor az alábbi kifejezés nem definiál egy függvényt:

func foo() { a = sqrt($1) ; print a }

Ehelyett egy szabályt generál minden rekordra, ami összefűzi a `func' változó tartalmát a `foo' függvény visszatérési értékével. Ha az eredmény szöveg nem üres, akkor végrehajtja a tevékenységet. Nem valószínű, hogy ezt akartad. (Az awk elfogadja ezt a programot, mint egy szintaktikailag helyes program, mivel a függvényt azelőtt lehet használni, mielőtt a függvény definiálva lett.)

Ha hordozható awk programot akarsz írni, akkor mindig a function kulcsszót használd a függvények definíciójánál.

Példák a függvénydefinícióra

Itt egy példa a felhasználó által definiálható függvényekre, myprint, ami egy számot kap argumentumként, és azt egy adott formában kinyomtatja.

function myprint(num)
{
  printf "%6.3g\n", num
}

Itt pedig bemutatjuk, hogy egy awk szabály hogyan használná a myprint függvényt:

$3 > 0 { myprint($3) }

Ez a program kinyomtatja minden bemeneti rekord harmadik mezőjét az általunk megadott formátumban, ha a mező pozitív számot tartalmaz. Így, ha a bemenet ez:

1.2 3.4 5.6 7.8
9.10 11.12 -13.14 15.16
17.18 19.20 21.22 23.24

akkor a program, az általunk megadott függvénnyel ezt fogja kinyomtatni:

5.6
21.2

Ez a függvény kitörli egy tömb minden elemét.

function delarray(a, i)
{
  for (i in a)
    delete a[i]
}

Amikor tömbökkel dolgozunk, gyakran szükség lehet egy tömb minden elemének a kitörlésére, és új elemekkel újrakezdeni. Ahelyett, hogy a törlő ciklust minden alkalommal le kellene írni, elég a fent definiált delarray függvényt meghívni.

Itt egy példa a rekurzív függvényre. A függvény egy szöveget vár argumentumként, és fordítva nyomtatja azt ki.

function rev(str, start)
{
  if (start == 0)
    return ""

  return (substr(str, start, 1) rev(str, start - 1))
}

Ha ez a függvény egy `rev.awk' nevű file-ban van, akkor így tesztelhetjük:

$ echo "Don't Panic!" |
> gawk --source '{ print rev($0, length($0)) }' -f rev.awk
-| !cinaP t'noD

Itt egy példa, ami az strftime beépített függvényt használja. A ctime függvény a C nyelvben egy időbélyeget vár argumentumként, és egy jól ismert formában nyomtatja azt ki. Itt az awk verziója:

# ctime.awk
#
# awk version of C ctime(3) function

function ctime(ts, format)
{
  format = "%a %b %d %H:%M:%S %Z %Y"
  if (ts == 0)
    ts = systime() # use current time as default
  return strftime(format, ts)
}

A felhasználó által definiált függvények hívása

Egy függvény hívása azt jelenti, hogy a függvény lefut, és elvégzi a munkáját. A függvényhívás egy kifejezés, és az értéke a függvény visszatérési értéke.

A függvényhívás a függvény nevéből és az utána álló, zárójelek közti argumentumok listájából áll. Amit argumentumként megadsz, azok is awk kifejezések; minden függvényhívásnál az argumentumként adott kifejezések kiértékelődnek, és az értékük lesz az aktuális argumentum. Például itt a foo függvényt hívjuk meg három argumentummal (ahol az első egy szövegösszefűzés):

foo(x y, "lose", 4 * z)

Figyelem: szóköz és tabulátor karakterek nem megengedettek a függvény neve és a nyitó zárójel között. Ha mégis lenne szóköz vagy tabulátor karakter a függvény neve és a nyitó zárójel között, akkor az awk ezt úgy is értelmezheti, mintha összefűzést szeretnél csinálni a függvény nevével és a zárójelek között megadott kifejezéssel. Ugyanakkor észreveszi, hogy függvény nevet adtál meg, nem változót, és így hibát fog jelezni.

Amikor a függvény meghívódik, akkor az argumentumként megadott értékek egy másolatát kapja meg. Ezt a módszert úgy is hívják, hogy hívás érték alapján. A függvényt hívó kifejezés akár változót is használhat argumentumként, de a hívott függvény ezt nem tudja: csak annyit tud a meghívott függvény, hogy az argumentum értéke micsoda. Például, ha ezt a kódot írod:

foo = "bar"
z = myfunc(foo)

akkor nem szabad azt gondolnod, hogy a myfunc függvény argumentuma a "foo változó", hanem hogy az argumentum a "bar" szöveg.

Ha a myfunc függvény megváltoztatja a lokális változók értékét, akkor az semmilyen hatással nincs más változókra. Így ha a myfunc függvény ezt csinálja:

function myfunc(str)
{
  print str
  str = "zzz"
  print str
}

akkor bár az első argumentumát, str, megváltoztatja, de a foo értéke nem fog megváltozni a hívó kifejezésben. A foo szerepe a myfunc függvény hívása során akkor ér véget, amikor az értékét, "bar", kiszámolta. Ha az str a myfunc függvényen kívül is létezik, akkor a függvény ennek a külső változónak az értékét nem tudja megváltoztatni, mivel a myfunc függvény futtatása ezt a változót "eltakarja".

Ugyanakkor ha a függvény argumentuma egy tömb, akkor az nem másolódik. Ehelyett, a függvény magát az eredeti tömböt tudja megváltoztatni. Ezt úgy nevezik, hogy hívás referencia alapján. A függvény belsejében végrehajtott változások egy tömbön, a függvényen kívül is érvényben lesznek. Ez nagyon veszélyes lehet, ha nem figyelsz oda, hogy mit csinálsz. Például:

function changeit(array, ind, nvalue)
{
  array[ind] = nvalue
}

BEGIN {
  a[1] = 1; a[2] = 2; a[3] = 3
  changeit(a, 2, "two")
  printf "a[1] = %s, a[2] = %s, a[3] = %s\n",
    a[1], a[2], a[3]
}

Ennek a programnak az eredménye: `a[1] = 1, a[2] = two, a[3] = 3', mivel a changeit függvény a "two" szöveget tárolja el az a tömb második elemében.

Néhány awk implementáció megengedi nem definiált függvények hívását, és csak a futtatás során jeleznek hibát, amikor a függvényt megpróbálják lefuttatni. Például:

BEGIN {
  if (0)
    foo()
  else
    bar()
}
function bar() { ... }
# figyelem: `foo' nem definiált

Mivel az `if' kifejezés soha nem igaz, ezért nem igazán probléma, hogy a foo függvény nem lett definiálva. Általában azért ez probléma, ha a program olyan függvényt akar meghívni, ami nem lett definiálva.

Ha a `--lint' opció szerepel a, akkor a gawk jelenteni fogja a nem definiált függvényeket.

Néhány awk implementáció hibát generál, ha a next kifejezést használod egy általad definiált függvényben. A gawk-nak nincs ilyen problémája.

A return kifejezés

A felhasználó által definiált függvények tartalmazhatják a return kifejezést. Ennek hatására a függvényből kilép, és folytatja az awk program futtatását a függvény hívása után. Visszatérési érték is megadható. Így néz ki:

return [expression]

Az expression rész opcionális. Ha nincs megadva, akkor a visszatérési érték nem definiált, és ezért nem megjósolható.

Minden függvénydefiníció végén egy egyszerű, argumentumok nélküli return kifejezést lehet feltételezni. Így ha a program eléri a függvény végét, akkor a függvény egy megjósolhatatlan értékkel visszatér. Az awk nem fog figyelmeztetni, ha egy ilyen függvény visszatérési értékét használod.

Néha azért írsz egy függvényt, amit csinál, és nem azért amit visszaad. Ezek a C nyelvben a void függvények, a Pascal-ban a procedure-k. Ezért hasznos, hogy nem adsz meg semmilyen visszatérési értéket; egyszerűen csak tartsd fejben, hogy ha egy ilyen függvény visszatérési értékét használod, akkor azt a saját felelősségedre teszed.

Itt egy példa, amelyik egy tömbben található legnagyobb számmal tér vissza:

function maxelt(vec, i, ret)
{
  for (i in vec) {
    if (ret == "" || vec[i] > ret)
      ret = vec[i]
  }
  return ret
}

A maxelt függvényt egy argumentummal kell meghívni, ami a tömb neve. Az i és a ret lokális változók, és nem argumentumok; bár semmi nincs ami meggátoljon abban, hogy egy második és egy harmadik argumentumot is megadj a függvénynek, de ekkor az eredmény furcsa is lehet. Az extra szóközök az i előtt jelzik, hogy ezután a lokális változókat definiálod. Ezt a konvenciót érdemes követned.

Itt egy program, amelyik a maxelt függvényt használja. A program betölt egy tömböt, meghívja a maxelt függvényt, majd kinyomtatja a tömb legnagyob elemét:

awk '
function maxelt(vec, i, ret)
{
  for (i in vec) {
    if (ret == "" || vec[i] > ret)
      ret = vec[i]
  }
  return ret
}

# Load all fields of each record into nums.
{
  for(i = 1; i <= NF; i++)
    nums[NR, i] = $i
}

END {
  print maxelt(nums)
}'

Az alábbi bemenetre:

1 5 23 8 16
44 3 5 2 8 26
256 291 1396 2962 100
-6 467 998 1101
99385 11 0 225

a program azt fogja mondani (megjósolható módon), hogy 99385 a legnagyobb szám a tömbben.

Beépített függvények

A beépített függvények olyan függvények, amelyek mindig az awk programod rendelkezésére állnak. Ez a fejezet definiálja az awk összes beépített függvényét; néhányat más bekezdésekben is megemlítettünk már.

Beépített függvények hívása

Ahhoz, hogy egy beépített függvényt meghívhassunk, a nevét kell leírni, majd az argumentumait zárójelek között. Például, `atan2(y + z, 1)' a atan2 függvényt fogja meghívni két argumentummal.

A függvénynév és a nyitó zárójel közötti szóközöket nem veszi figyelembe az awk, de azt tanácsoljuk, hogy ne használj szóközöket itt. A felhasználó által definiálható függvények hívásánál a szóközök nem megengedettek, és ha nem használsz szóköz karaktert a függvénynév és a nyitó zárójel között, akkor könnyebb ezt a hibát elkerülni.

Minden beépített függvény elfogad egy adott számú argumentumot. Bizonyos esetekben, az argumentumok elhagyhatók. Hogy mi történik akkor, amikor egy függvénynek nem adunk meg argumentumot, az függvényről függvényre változik, és az adott függvénynél ezt bemutatjuk. Néhány awk implementáció a függvénynek átadott extra argumentumokat nem veszi figyelembe. Ugyanakkor a gawk esetén, ez az eset egy végzetes hibát fog generálni.

Amikor egy függvény meghívásra kerül, akkor az argumentumait adó kifejezések először teljesen kiértékelődnek, és csak utána történik meg függvényhívás. Például az alábbi programrészletben:

i = 4
j = sqrt(i++)

a i változó értéke öt lesz mielőtt az sqrt függvény meghívódik. A függvény argumentuma négy lesz.

A függvény argumentumainak kiértékelési sorrendje nem definiált. Így, nem szabad arra számítani, hogy az argumentumok balról jobbra vagy jobbról balra értékelődnek ki. Például:

i = 5
j = atan2(i++, i *= 2)

Ha a kiértékelés balról jobbra halad, akkor az i értéke először hat lesz, majd 12 és az atan2 argumentumai hat és 12 lesz. De ha a kiértékelés jobbról balra halad, akkor az i értéke először 10 lesz, majd 11, és az atan2 argumentumai 11 és 10 lesz.

Numerikus beépített függvények

Az alábbiakban megadjuk a számokkal dolgozó beépített függvények listáját. Az opciónális argumentumokat szögletes zárójelek veszik körül ("[" és "]").

Szövegmanipuláló beépített függvények

Ebben a bekezdésben található függvények szövegeket vizsgálnak vagy változtatnak meg. Az opcionális argumentumok szögletes zárójelek között ("[" and "]") jelennek meg.

További információ a `\' és a `&' karakterek használatáról a sub, a gsub és a gensub függvényekben

Amikor a sub, a gsub vagy a gensub függvényen belül a `\' vagy a `&' karaktert szeretnéd használni, akkor fontos arra emlékezni, hogy az escape karakterek feldolgozásának több szintje van.

Először is van a lexikális szint, amikor is az awk beolvassa a programodat és felépíti a programod belső reprezentációját, amit majd végre tud hajtani.

Ezután van a futtatási szint, amikor az awk azt ellenőrzi a csereként szolgáló szövegekben, hogy mit is kell csinálnia.

Mindkét szinten az awk ellenőrzi, hogy milyen karakter következik a `\' karakter után. A lexikális szinten escape szekvenciákat keres. Így minden `\' karakterért, amit az awk majd a futtatási szinten vesz figyelembe két `\' karaktert kell megadni a lexikális szinten. Ha egy olyan karakter követi az `\' karaktert, ami nem egy érvényes escape szekvencia, akkor a Unix awk és a gawk is egyszerűen eltávolítja a `\' karaktert, és csak a követő karaktert tartja meg a szövegben. Így például, a "a\qb" szöveg a "aqb" szövegnek felel meg.

A futtatási szinten a különböző függvények különböző módon kezelik a `\' és a `&' karakterek használatát. A helyzet (sajnos) elég bonyolult.

Történelmileg a sub és a gsub függvények a `\&' karaktersorozatot speciálisan kezelték; ezt a sorozatot a csereként használt szövegben lecserélte a `&' karakterre. Minden más `\' karakter a csereként szolgáló szövegen belül, amit nem a `&' karakter követett, változatlan maradt. Ezt az alábbi táblázat illusztrálja:

Ez a táblázat bemutatja mind a lexikális feldolgozást, ahol a páratlan számú `\' karakterek páros számúvá válnak a futtatási szinten, és a sub által végrehajtott feldolgozást a futtatási szinten. (Az egyszerűség kedvéért a későbbi táblázatokban csak a lexikális szinten páros számú `\' karakterrel megadott eseteket mutatjuk be.)

A probléma a történelmi megközelítéssel az, hogy lehetetlen azt az esetet megadni, amikor az illeszkedő szöveget egy `\' karakter előzi meg.

Az 1992-es POSIX szabvány megpróbálta ezt a problémát megoldani. A szabvány azt mondja ki, hogy a sub és a gsub függvények a `\' karakter után vagy egy `\' vagy egy `&' karaktert keresnek. Ha bármelyik eset előfordul, akkor a `\' karaktert követő karakter érintetlenül kerül be a szövegbe. Tehát a `\' és a `&' karakterek értelmezése a következő:

Ez úgy tűnik megoldja a problémát. Sajnos, a szabvány szövege szokatlanul fogalmazza ezt meg. Azt mondja, hogy a `\' karakter kikapcsolja bármely követő karakter speciális jelentését, de a speciális jelentés kivéve a `\' és a `&' karaktereket nem definiált. Ez a fogalmazás két problémához vezet.

  1. A `\' karaktert kétszer kell leírni a replacement szövegben, aminek hatására a régi awk programok nem fognak helyesen működni.
  2. Ha biztosak akarunk abban lenni, hogy az awk program hordozható, akkor a replacement szövegben minden karakter elé egy `\' karaktert kell tenni.

A POSIX szabvány átdolgozás alatt áll. A fenti problémák miatt, az új, módosított változat olyan szabályokat ad meg, amelyek jobban hasonlítanak a régiekhez. Az új szabály speciális eseteket definiál arra az esetre, ha egy `\' karakternek kell megelőznie az illeszkedő szöveget.

Röviden, a futtatási szinten most három speciális eset van, a `\\\&', a `\\&' és a `\&'. Ezzel ellentétben régen csak egy volt. Ugyanakkor, mint a régi szabályban, bármely `\' karakter, ami nem része a három kivételnek nem speciális, és maga a karakter jelenik meg.

A gawk 3.0-ás verziója az új POSIX ajánlást követi a sub és a gsub esetén. Jelenleg még nem lehet tudni, hogy az új ajánlás végleg bekerül-e a szabványba. A gawk jövőbeli verziói a szabványt fogják követni, bármi is legyen a végleges szabványban; ekkor ezt a könyvet is frissíteni fogjuk.

A szabályok a gensub esetén sokkal egyszerűbbek. A futtatási szinten, amikor a gawk egy `\' karaktert vesz észre, és a követő karakter egy számjegy, akkor a reguláris kifejezésben elhelyezett zárójelek a közötti illeszkedő szöveg fog megjelenni a kimenetben. Minden más esetben, akármi is legyen a `\' karakter után, maga a karakter fog megjelenni és a `\' karakter nem.

A lexikális és a futtatási szint bonyolultsága és a sub és a gsub függvények speciális esetei miatt azt tanácsoljuk, hogy ha gawk-ot használsz, akkor a gensub függvényt használd helyettesítésre.

Bemenettel és kimenettel (I/O) kapcsolatos beépített függvények

Az alábbi függvények a bemenettel vagy a kimenettel kapcsolatosak. Az opcionális argumentumok szögletes zárójelek ("[" and "]") között jelennek meg.

1 Interaktív és nem interaktív bufferelés

Érdemes itt megjegyezni, hogy a bufferelés zavarba ejtő is lehet ha a programod interaktív; pl. a program kommunikál a felhasználóval.

Az interaktív programok általában soronként bufferelik a kimenetüket. A nem interaktív programok addig várnak amíg be nem tellik a bufferük, ami több sornyi kimenet is lehet, és csak ekkor írják ki a buffer tartalmát.

Itt egy példa a különbség bemutatására.

$ awk '{ print $1 + $2 }'
1 1
-| 2
2 3
-| 5
Control-d

Minden kimeneti sort azonnal kinyomtat. Hasonlítsd ezt össze a következő példával.

$ awk '{ print $1 + $2 }' | cat
1 1
2 3
Control-d
-| 2
-| 5

Itt addig nem ír ki semmit, amíg a Control-d billentyű kombinációt meg nem kapja, mivel minden bufferelt, és egy menetben küld el mindent a cat parancsnak a csövön keresztül.

A kimeneti bufferelés szabályozása a system függvénnyel

Az fflush függvénnyel közvetlen szabályozható az egyes file-ok vagy csövek bufferelése. Ugyanakkor sok más awk implementációban ez a függvény nem támogatott. Egy alternatív megoldás a bufferek kiürítésére, ha a system függvényt hívjuk meg egy üres szöveg argumentummal:

system("") # kimenet ürítése

A gawk a system függvény ilyen használatát speciálisan kezeli, és van annyira okos, hogy nem futtat egy shell-t (vagy más parancsértelmezőt) egy üres paranccsal. Ezért a gawk esetén ez nem csak hasznos megoldás, de hatékony is. Bár ez a megoldás más awk implementációkban is működni fog, de nem biztos, hogy azok az awk implementációk nem indítanak el egy shell-t. (Más implementációk lehet hogy csak a szabványos kimenetet ürítik, és nem minden bufferelt kimenetet.)

Ha arra gondolsz, hogy egy programozó mit várna el, akkor egyértelmű, hogy a system függvény üríti a még ki nem nyomtatott buffereket. Az alábbi program:

BEGIN {
  print "first print"
  system("echo system echo")
  print "second print"
}

ezt kell, hogy nyomtassa

first print
system echo
second print

és nem ezt

system echo
first print
second print

Ha az awk nem üríti a buffereit a system függvény hívása előtt, akkor az utóbbi (nem kívánt) eredményt kapnánk.

Dátumfeldolgozó függvények

Az awk programok egyik gyakori felhasználása a log file-ok feldolgozása, amelyek dátum és idő információt is tartalmaznak. Sok program a time rendszerfüggvény által megadott időt használja a log file-okban. Ez az idő, a POSIX rendszereken, az 1970 január 1-e (UTC), éjfél óta eltelt másodpercek száma.

Azért hogy ezeket a log file-okat egyszerűbb legyen feldolgozni, a gawk két függvényt biztosít az időbélyegek kezelésére. Mind a két függvény gawk kiterjesztés; sem a POSIX szabvány nem tartalmazza, sem más awk implementációk.

Az opcionális paraméterek szögletes zárójelek ("[" and "]") között jelennek meg.

A systime függvény lehetővé teszi, hogy időbélyegeket hasonlíts össze az aktuális időponttal. Például könnyű meghatározni, hogy mennyivel korábban lett az adott log bejegyzés rögzítve.

Az strftime függvény lehetővé teszi, hogy ember számárá is olvasható formában jelenjen meg az időbélyeg. Hasonló az sprintf függvényhez, mivel minden nem formátum leíró karaktert ugyanúgy bemásol a szövegbe és csak a formátum leírók helyeire teszi be az adott időpont, adott komponensét.

Az ANSI C szabvány által garantált, hogy az strftime függvény az alábbi formátumleírókat támogatja:

Ha a formátum leíró nem a fentiek közül valamelyik, akkor a viselkedés nem definiált.

Informálisan, a helyi azt a földrajzi helyet jelenti, ahol a programnak futnia kell. Például az 1991 szeptember 4-ei dátum egy általános rövidítése az Egyesült Államok területén a "9/4/91". Európa sok országában a "4.9.91" rövidítést használnák. Így a `%x' a `9/4/91' szöveget produkálja ha az "US" van definiálva, míg az "EUROPE" esetén az eredmény a `4.9.91' lenne. Az ANSI C a "C"-t definiálja mint helyi beállítás (locale), amit a legtöbb C programozó használ.

A gawk csomag a strftime függvény egy "public-domain" C verzióját is tartalmazza olyan rendszerek számára, amelyek nem teljesen ANSI kompatíbilisek. Ha a gawk ezzel a verzióval lett lefordítva, akkor az alábbi formátumleírókat is lehet használni:

Ez a példa a POSIX date segédprogram awk implementációja. Általában a date az aktuális dátumot és időt nyomtatja ki egy jól ismert formátumban. Ugyanakkor, ha olyan argumentumot adsz meg ami `+' karakterrel kezdődik, akkor a date a nem formátumleíró karaktereket közvetlenül kinyomtatja, míg az adott formátumnak megfelelően írja ki az aktuális időt. Például:

$ date '+Today is %A, %B %d, %Y.'
-| Today is Thursday, July 11, 1991.

Itt a gawk verziója a date segédprogramnak. Egy shell "wrapper" veszi körbe, hogy a `-u' opciót is kezelje, ami azt adja meg a date programnak, hogy UTC időzónában adja meg az időt.

#! /bin/sh
#
# date --- approximate the P1003.2 'date' command

case $1 in
-u) TZ=GMT0 # use UTC
  export TZ
  shift ;;
esac

gawk 'BEGIN {
  format = "%a %b %d %H:%M:%S %Z %Y"
  exitval = 0

  if (ARGC > 2)
    exitval = 1
  else if (ARGC == 2) {
    format = ARGV[1]
    if (format ~ /^\+/)
      format = substr(format, 2) # remove leading +
  }
  print strftime(format)
  exit exitval
}' "$@"

Eljárásabsztrakció

Az AWK-ban nem lehet létrehozni eljárásokat, csakis függvényhívásokkal (beépített vagy saját) élhetünk. Tehát nem támogatja az eljárásabsztrakciót.

Alprogram

Mivel nem támogatja az eljárásabsztrakciót, az egyéni alprogramok írására csak saját függvénydeklarációval van lehetőség. Ekkor a függvénydefiníciós részben megadott módon lehet függvényeket deklarálni, és alkalmazásukkal lehet a program szerkezetét egyszerűsíteni áttekinthetőbbé, esetleg újrafelhasználhatóvá tenni.

Alprogram beágyazhatósága

Az AWK-ban az alprogramokat (függvényeket) blokkszerűen definiáljuk a kapcsoszárójelek között. Láthatóságuk természetesen globális, a program minden részéből meg lehet hívni a függvényeket, de a függvényekben használt változók élettartama, csak a függvényhívás ideje alatt tart, a függvényhívás után ezen változók nem elérhetőek, értékük definiálatlan.

Paraméterek

Érték szerinti paraméterátadás

Ha a függvény hívása elemi típussal (paraméterrel) történik, akkor automatikusan érték szerinti paraméterátadás történik, tehát a változó értéke csak lemásolódik, és a függvényen belül hiába változtatjuk meg az értékét, a függvények kívül ennek semmilyen hatása nem lesz a program futására. Az AWK-ban mindig érték szerinti paraméterátadás van, nem tudunk kikényszeríteni, mint a C-ben, cím szerinti paraméterátadást. Kivéve lásd alább!

Cím szerinti paraméterátadás

Cím szerinti paraméterátadás történik akkor, ha tömböt adunk át paraméterként. Ilyenkor nem a változó értéke, hanem a címe adódik át a függvénynek, és ha a függvényen belül megváltoztatjuk a tömb értékét, akkor az az érték a függvényen kívül is megmarad, és a program a további működése során, már ezzel a megváltozott értékkel fogja végezni a további műveleteket.

Eredmény szerinti paraméterátadás

Eredmény szerinti paraméterátadás a függvény visszatérési értéket. Az AWK-ban csak függvények vannak, melynek mindig van visszatérési értéke, ez csak akkor léteuik, miután a függvény elvégezte a feladatát, befejezte a működését. Ekkor a program lefoglal egy memóriacímet a visszatérési értéknek, és ebbe a címbe írja az értéket. Ha egy másik változónak adjuk az értéket, akkor ez az érték másolódik be, a másik változóba.

Érték/Eredmény szerinti paraméterátadás

Sajnos az érték eredmény szerinti paraméterátadás nincsen az AWK-ban, az előbb leírt okok miatt.

Operátorok

Az operátorok többségét is a C-ből kölcsönözte az AWK. (pl.: ++ ?:)

Rekurzió

Rekurziót lehet definiálni a fent felsorolt eszközökkel. Azaz úgy, hogy definiálunk egy függvényt, mely mindig meghívja önmagát, ha a visszatérési értéke FALSE. Erre van lehetőség, de használata nem javasolt, mert így a memória igen hamar megtelhet, és akkor a program abnormális működéssel fog leállni (Abortálja az op. rendszer). Minden rekurzív függvénnyel megoldható problémát meg lehet oldani a neki megfelelő rekurzív függvény nelküli programmal is.

Beépített adattípusok

Beépített változók

A legtöbb awk változó saját használatra mindig elérhető; az értékük soha nem változik meg, kivétel ha a program értéket rendel hozzá, és soha nem befolyásol semmit, amikor az értékét lekérdézzük. Ugyanakkor néhány awk változónak speciális előre definiált jelentése van. Ezek közül néhányat az awk automatikusan többször megvizsgál, így ezek segítségével az awk működését befolyásolni lehet. Másokat az awk automatikusan beállít, így információt továbbít az awk belső struktúrájából a programod felé.

Ez a fejezet dokumentálja az gawk összes beépített változóját. A legtöbbjük dokumentálva van még a hatásukat leíró fejezetekben is.

Az AWK-t befolyásoló beépített változók

Az alábbiakban azoknak a változóknak az ábécé sorrendbe szedett listáját közöljük, amelyek segítségével az awk működése befolyásolható. Azok a változók, amelyek csak a gawk-ban találhatók meg egy csillaggal `*' vannak megjelölve.

Információt hordozó beépített változók

Az alábbiakban azoknak a változóknak az ábécé sorrendbe szedett listáját közöljük, amelyeket az awk automatikusan állít be, és így információt biztosít a programod számára. Azok a változók, amelyek csak a gawk-ban használhatók egy csillaggal `*' vannak megjelölve.

Egy megjegyzés az NR és az FNR változókról. Az awk egyszerűen megnöveli ezeket a változókat, amikor egy új rekordot olvas be, ahelyett, hogy a beolvasott rekordok számának abszolút értékét tárolná a változókban. Ez azt jelenti, hogy ha a programod megváltoztatja valamelyik változó értékét, akkor ezt az új értéket fogja megnövelni, amikor egy új rekordot olvas be (s.s.). Például:

$ echo '1
> 2
> 3
> 4' | awk 'NR == 2 { NR = 17 }
> { print NR }'
-| 1
-| 17
-| 18
-| 19

Mielőtt az FNR változó bekerült az awk nyelvbe, sok awk program használta ezt a lehetőséget arra, hogy számon tartsa az egy file-ból beolvasott rekordok számát; amikor a FILENAME változó értéke megváltozott az NR értékét zérusra állították.

Az ARGC és az ARGV változók használata

$ awk 'BEGIN {
> for (i = 0; i < ARGC; i++)
> print ARGV[i]
> }' inventory-shipped BBS-list
-| awk
-| inventory-shipped
-| BBS-list

Ebben a példában az ARGV[0] értéke "awk", az ARGV[1] értéke "inventory-shipped" és az ARGV[2] értéke "BBS-list".

Érdemes megfigyelni, hogy maga az awk program nem került bele az ARGV tömbbe. Más speciális parancssori opciók, az argumentumaikkal együtt, szintén nem kerülnek be a tömbbe. Ilyenek például az értékadó utasítások a `-v' opcióval. Normális parancssori értékadó utasítások az argumentum részei és megjelennek az ARGV tömbben.

$ cat showargs.awk
-| BEGIN {
-| printf "A=%d, B=%d\n", A, B
-| for (i = 0; i < ARGC; i++)
-| printf "\tARGV[%d] = %s\n", i, ARGV[i]
-| }
-| END { printf "A=%d, B=%d\n", A, B }
$ awk -v A=1 -f showargs.awk B=2 /dev/null
-| A=1, B=0
-| ARGV[0] = awk
-| ARGV[1] = B=2
-| ARGV[2] = /dev/null
-| A=1, B=2

A programod megváltoztathatja az ARGC változó értékét és az ARGV tömb elemeit. Minden alkalommal, amikor az awk eléri egy file végét, a következő file nevét az ARGV tömb következő eleméből veszi. Ha ezt az értéket a programod megváltoztatja, akkor a programod az általad megadott file-t fogja feldolgozni. A "-" szöveg használata esetén a szabványos bemenetről fog olvasni. Extra elemek tárolásával és az ARGC megnövelésével további file-ok feldolgozására lehet utasítani az awk-ot.

Ha az ARGC értékét csökkentjük, akkor bemeneti file-t távolíthatunk el a parancssori listából. Ha az ARGC régi értékét elmentjük egy másik változóba, akkor az eldobott argumentumokat a program felhasználhatja.

Ahhoz, hogy a lista közepéről távolítsunk el egy file nevet, az ARGV adott elemét üres szöveggel ("") kell feltölteni a file neve helyett. Ez egy speciális lehetőség, mivel az awk nem veszi figyelembe az üres szöveggel megadott file neveket. Másik lehetőség a delete kifejezés használata, ami eltávolít egy elemet az ARGV tömbből.

Tipikusan ezt a feldolgozást a BEGIN szabályon belül érdemes elvégezni mielőtt a bemeneti file-ok feldolgozása megkezdődik, ami mindkét módszert bemutatja az ARGV egy elemének eltávolítására.

Az alábbi programrészlet az ARGV tömböt dolgozza fel és eltávolítja a parancssori opciókat.


BEGIN {
  for (i = 1; i < ARGC; i++) {
    if (ARGV[i] == "-v")
      verbose = 1
    else if (ARGV[i] == "-d")
      debug = 1
    else if (ARGV[i] ~ /^-?/) {
      e = sprintf("%s: unrecognized option -- %c",
        ARGV[0], substr(ARGV[i], 1, ,1))
      print e > "/dev/stderr"
    } else
      break
    delete ARGV[i]
  }
}

Ahhoz, hogy tényleg csak az opciókat vegye figyelembe, az awk opciókat egy `--' kifejezéssel kell lezárni és azután kell megadni az opcióidat, például így:

awk -f myprog -- -v -d file1 file2 ...

Erre nincs szükség a gawk esetén: Hacsak a `--posix' opció nem lett megadva, a gawk minden, fel nem ismert opciót az ARGV tömbben helyez el, hogy az awk program dolgozza fel őket.

Amint a gawk nem ismer fel egy opciót, azonnal befejezi az opciók feldolgozását. A fenti példa így nézne ki gawk-ban:

gawk -f myprog -d -v file1 file2 ...

Mivel a `-d' nem egy érvényes gawk opció ezért a további `-v' opció átadódik az awk programnak.