Awk

Az awk egy olyan programozási nyelv, amit szöveges állományok feldolgozására terveztek.
Egy tipikus awk programot (szkript) egy interpreter olvas be és hajt végre. A szkript végrehajtása során a feldolgozott szöveges állomány(ok)at (tartalmának változatlanul hagyásával) másféle kimenetté formálja át.
A bemeneti adatok változatlanul hagyása biztosítja, hogy ugyanazon adatokra (például szövegfájlok) többféle awk program is futtatható egymás után, vagyis ugyanazon fájl(ok)ból többféle adat is kinyerhető legyen.

Története 
Elnevezése a megalkotói – Alfred Aho, Peter Weinberger és Brian Kernighan – családneveinek kezdőbetűiből született. Az eredeti awk az 1977-ben az AT&T Bell laboratóriumában készült. Ideális szöveges állományok szűrésére, átformálására és kiértékelésére. Ma is minden Unix rendszeren van legalább egy awk változat. A Free Software Foundation a gawk nevű változatot gondozza, a Linux disztribúciókkal is jellemzően a gawk implementációt szállítják. Windows rendszerekhez is letölthető a gawk például a Cygwin részeként.
Elérhető MS-DOS, Atari és Amiga operációs rendszerek alatt is. Megszületése óta számos bővítése létezik, például QTAwk, Xgawk (awk+xml), Jawk (Java alapú) stb.
Az awk nyelv ereje, rövidsége, de az awk szkriptek határainak felismerése adta Larry Wallnak az ötletet a Perl nyelv kifejlesztésére.
A futási sebesség növelése miatt kísérletek történtek awk programok fordítására is, azonban az eltérő utasításkészlet miatt például csak a tawk-szkript fordítható le futtatható kóddá.

Egy awk program működése 
Egy awk szkript futtatásához szükség van egy awk nyelvet feldolgozó (gawk, nawk stb.) interpreterre, a megfelelő szövegállomány(ok)ra és többnyire egy szkriptre, amely a tevékenységeket írja le. A nyelv tömörsége miatt néhány program olyan rövid, hogy parancssori paraméterként is beadható az interpreter részére. Alapértelmezés szerint az interpreter a szabványos bemenetről olvassa a feldolgozandó adatokat (stdin) és az eredményeket a szabványos kimenetre írja (stdout), azonban átirányításokkal a működése kiterjeszthető.

Az awk adatvezérelt nyelv: minták és tevékenységek sorozatából áll. Beolvas egy sort, automatikusan mezőkre (szavakra) darabolja és a megadott feltételek szerint elvégzi a minta egyezőségének vizsgálatát. Ha van egyezés, akkor a mintához tartozó egy vagy több tevékenységet hajtja végre, majd a szabványos kimenetre írja. Ezután a következő sor feldolgozása történik a fentiek szerint, míg csak a fájl végére nem ér.

Egy awk program részei 
Egy awk program a következő részekből állhat:
 előkészítő rész;
 saját függvénydefiníciók;
 programtörzs;
 befejező rész.

Fenti részekből a program sajátosságai szerint tetszőleges részek elhagyhatóak, ha az adott program nem igényli (például nincs előkészítésre szükség vagy nincsenek saját függvények, stb.).

Előkészítő rész 
Jellemzően értékadó, vagy programfutást előkészítő parancsok találhatók ebben a részben.
BEGIN { parancsok }
#Adatbeolvasás előtt BEGIN kapcsos zárójelei közötti parancsokat hajtja végre.
#A BEGIN speciális mintaként is értelmezhető.

Saját függvénydefiníciók 
A programok szerkesztését, futtatását megkönnyíti, ha a felhasználó új utasításokat hozhat létre a meglévő nyelvi elemek felhasználásával.

Programtörzs 
A legegyszerűbb awk programok csak a programtörzset tartalmazzák és általában mintából és a mintához tartozó parancsokból, más néven tevékenységből állnak:
/1. minta/ { parancsok }
/2. minta/ { parancsok }
/minta/
# Ha nincs külön parancs megadva, a mintának megfelelő sort kinyomtatja  a standard outputra.
{ tevékenységek }
# Ha nincs minta megadva, a tevékenységeket minden sorra egymás után végrehajtja.

Az awk soronként olvassa a bemenetet, ezért az awk program működéséhez kis operatív memória is elegendő. Minden beolvasott sort összehasonlít a mintákkal, és abban az esetben, ha illeszkedést talál, a mintához tartozó parancsokat végrehajtja. A mintákat a szabályos kifejezések szabályai szerint fejti ki, vagyis a minták reguláris kifejezéseket is tartalmazhatnak.

Befejező rész 
Az összes adat feldolgozása és a többi parancs végrehajtása után a befejező rész parancsait hajtja végre. Jellemzően összegző- és formázó utasítások találhatók ebben a részben. Az END speciális mintaként is értelmezhető.
END {tevékenységek}

Jelkészlet 
Egy awk program nyelvi elemei az angol abc kis- és nagybetűit, számokat, írásjeleket és az aláhúzásjelet tartalmazhatják. A kis- és nagybetűvel jelölt azonosítók különbözőek.

Nyelvi elemek 
Az awk számos nyelvi elemet használ a programozás megkönnyítésére, így például:
 változók,
 rekordok és mezők,
 tömbök,
 minták,
 tevékenységek,
 operátorok,
 vezérlő utasítások,
 be- és kimeneti utasítások,
 belső függvények,
 saját függvények.
 konstansok.

Változók 
Egy awk szkript változói lehetnek
 felhasználó által létrehozott változók és
 előre definiált (belső) változók.
Az awk változói globális változók és azáltal jönnek létre, hogy használni kezdik őket, tehát dinamikus változók. A változók értéke lehet sztring vagy lebegőpontos szám, a használat módjától függően. Léteznek lokális változók is, amelyek csak az adott saját függvényen belül láthatóak, ezeket a függvény paraméterlistája után kell felsorolni.

Belső változók

A belső változók mindig globálisak és a program működését befolyásolják. Megállapodás szerint a belső változókat mindig nagybetűkkel jelölik.
Példák belső változókra:
 NR (Number of Records) A beolvasott rekordok (sorok) számát tartja nyilván.
 NF (Number of Fields) A beolvasott és feldolgozás alatt lévő rekord (sor) mezőinek (szavainak száma) Az utolsó mező a $NF.
 FILENAME A feldolgozás alatt álló fájl neve.
 FS (Field Separator) A mezőelválasztó karaktert tartalmazza, amely mentén az awk elvégzi a $0 tartalmának darabolását, alapértelmezés szerint a szóköz és a tabulátor jele. FS ="," hatására a mezők szétvágása nem szóközök mentén történik, hanem a vesszőkig (például CDF-formátum feldolgozása). A beolvasott rekord mezőelválasztó karakterei nem kerülnek feldolgozásra és tárolásra, csupán egy jelzés a feldolgozónak.
 RS (Record separator) Alapértelmezés szerint a sorvége jel, de tetszés szerint megváltoztatható.
 OFS (Output Field Separator) a kiírt mezőket (szavakat) elválasztó karakter. Alapértelmezés szerint a szóköz.
 ORS: (Output Record Separator) a kiírt rekordok (sorok) közötti határolójel. Alapértelmezés szerint az újsor jel.
 OFMT (Output Format) A számok kiírási formátumát tartalmazza. Alapértelmezés szerint OFMT = "%.6g".

Rekordok és mezők 
A feldolgozott szövegfájl egy sorát rekordnak nevezik. Egy rekord az RS belső változó által meghatározott karakterig tart. RS = "."  esetén a rekord hossza éppen egy mondat, függetlenül az újsor jeltől.
Vannak előre megadott belső változók, amelyek minden rekord (sor) feldolgozásakor automatikusan értéket kapnak. Például:
 $0 az éppen feldolgozás alatt álló rekordot tartalmazza, vagyis a alapértelmezés szerint a feldolgozás alatt álló sort;
 $1, $2, …$n :a sor egyes elemei, más szavakkal a rekord mezői; alapértelmezés szerint egymástól szóközzel vagy tabulátorjellel elválasztott részei (szavak). A mezők automatikusan jönnek létre a rekord beolvasása során. Ez az automatikus felbontás nagyon egyszerűvé és hatékonnyá teszi az adatfeldolgozást. A mezőkre hivatkozni sorszámukkal lehet:
t = $2 *5
# a t változó a második mező ötszörös értékét veszi fel
k = $1 + $3
# a k változóban az első és a harmadik mező összege tárolódik

A mezők indexe csak pozitív egész lehet, negatív mezőhivatkozások (például $-2) fatális hibát okoznak. Az aktuális rekordban lévő mezők számát az NF belső változó tartalmazza. Értéke rekordról rekordra változhat.

Tömbök 
A tömböket az awk-ban szögletes zárójelekkel []jelölik és minden tömb asszociatív (azaz sztringekkel indexelt). A tömb egyes részeit elválasztó karakter a SUBSEP belső változóban tárolódik. Lehetséges többdimenziós tömbök használata is.

Minták 
Az awk minták a következők lehetnek:
 /reguláris kifejezés/
 relációs kifejezés
 minta && minta
 minta || minta
 minta ? minta : minta
 (minta)
 ! minta
 minta1, minta2

A legtöbb leírás speciális mintaként tárgyalja a BEGIN és az END részeket. Sajnos ezek végrehajtása kötött és nem keverhetők más mintákkal, továbbá nem érvényes rájuk az awk mintákra jellemző inputfüggés sem: a BEGIN törzse azelőtt hajtódik végre, mielőtt a bemeneti adatok feldolgozása elkezdődne, az END törzse pedig azután, ha minden bemeneti adat feldolgozása befejeződött. Ezért néhány leírás nem is tekinti őket mintáknak, hanem előkészítő és befejező részként tárgyalja. A BEGIN és az END részek törzse nem hiányozhat.

Tevékenységek 
A tevékenységek utasításai kapcsos zárójelek között szerepelnek.

Operátorok 
Az awk operátorai között is érvényes a műveleti sorrend (precedencia), amely szerint összetett kifejezésekben először a magasabb precedenciájú műveletek értékelődnek ki. Az AWK operátorai csökkenő precedencia szerint:

Vezérlő utasítások 
Legegyszerűbb esetben a vezérlő utasítás(oka)t kapcsos zárójelben lehet elhelyezni. Egy sorban több utasítás is elhelyezhető, ha használják az utasításokat lezáró pontosvesszőt. Példák:
{ print $1 }
{ print $1, $3; print i}
Vezérlő utasításokkal feltételek vizsgálata is elvégezhető vagy ciklusok, ugrások hajthatók végre vagy a tömb(ök) elemei vizsgálhatók, törölhetőek:
 if (feltétel) utasítás [ else utasítás ]
 while (feltétel) utasítás
 do utasítás while (feltétel)
 for (kif1; kif2; kif3) utasítás
 for (var in array) utasítás
 break
 continue
 delete array[index]
 delete array
 exit [ kifejezés ]

Be- és kimeneti utasítások 
Az awk mindössze néhány be- és kimeneti utasítással rendelkezik, mint például a getline, print, printf, next.

Belső függvények 
A belső függvények numerikus- sztring- és időfüggvények.

Saját függvények 
Saját függvény definiálása a function kulcsszó segítségével történhet. Néhány awk-változat elfogadja a rövidebb, négybetűs func alakot is. Például a valami nevű függvény definíciója a következők szerint épül fel:
function valami(paraméterek__és_lokális_változók)
{
  függvénytörzs
}

Paraméterek és lokális változók

Ha a saját függvény egy vagy több paramétert vesz át, akkor azokat a függvény neve utáni zárójelben kell megadni, vesszővel elválasztva, hasonlóan a c-nyelvhez. A következő példa három paramétert vár (a, b, és c) továbbá egy lokális változója is van (az i):
function add(a,b,c, i)
{
 i= 2;
  függvénytörzs
 # a függvényből való kilépéskor törlődik az i változó értéke (lokális)
}

Megállapodás szerint a lokális változót egy tabulátor választja el a függvény paramétereitől (megkönnyíti az olvasást).
A lokális változók csak a függvény lezáró kapcsos zárójeléig „élnek” és automatikusan megszűnnek. Amennyiben egy változó neve a függvény paraméterei után nincs felsorolva, automatikusan globális változóvá válik:
function add(a,b,c)
{
 i = 2;
  függvénytörzs
 # a függvényből való kilépéskor megőrződik az i változó értéke (globális)
}

Függvénytörzs

A függvény törzsében minden awk-művelet megengedett, beleértve a szabályos kifejezést, értékadást, függvényhívást, mintakeresést és feldolgozást, stb.
A függvénytörzsben a rekurzió megengedett. Visszatérési érték a return utasítás után adható meg. Amennyiben a return után nem áll paraméter, úgy a visszatérési érték határozatlan, mint a c-nyelv void-típusú függvényeiben.
A return számára lokális változó értéke is átadható, mert a lokális változó értékének törlése előtt az érték visszaadására szolgáló memóriaterületre. Előrehivatkozás megengedett,
mivel az interpreter először beolvassa az egész programot, és – hibamentes program esetén – csak a teljes program beolvasása után kezdi el végrehajtani. Egy saját függvény definíciója tehát bárhol előfordulhat az awk program szabályai szerint. Így nem fordulhat elő, hogy egy awk-függvény a hívás során még nem ismert: például ha a programban a hívás helye után van definiálva.

Egy egyszerű függvénydefiníció lehet például a következő:
 function add_three (number, temp) {
   temp = number + 3
   return temp # temp értékének átmásolása a return területére
 } # temp nevű lokális változó törlése
Egy definiált saját függvény a nevének megadásával hívható meg. Saját függvény megadható paraméterként is: A következő példában a print utasítás paramétereként kapja az add_three függvényt, amely pedig egy numerikus értéket kap paraméterként (esetünkben a 36-ot). Eredményeképpen 39 íródik a kimenetre:
 print add_three(36)

Konstansok 
Az awk kizárólag sztring konstansokat használ, amelyek vezérlésre szolgálnak. A sztring konstansokat idézőjelben kell megadni, például az újsor jele "\n"

Awk parancsok 
Az awk nyelv szintaxisa hasonlít a C nyelvéhez, ami nem csoda, hiszen az awk nyelv egyik szerzője, Brian W. Kernighan a C nyelv megalkotója.
Elemi parancsok a változóknak való értékadás, a változók összehasonlítása, az elágazás (if…else) és a ciklus (for, while). Emellett belső függvényeket, illetve saját implementálású függvényeket is meg lehet hívni.
Az adatokat a "print" paranccsal lehet kinyomtatni, azaz a kimenetre írni. Például egy sor második mezője a
  print $2
paranccsal íratható ki a szabványos kimenetre.

Példák

Helló, világ! 
A „Helló, világ!” program awk-ban a következőképpen implementálható:
 BEGIN { print "Helló, világ!" }

Adott karakterszámnál hosszabb sorok kiíratása 
A következő programmal a 80 karakternél hosszabb sorokat írja ki. Vegyük észre, hogy az alapértelmezett akció a sor kiírása:
 length > 80

Minden n. sor kiírása 
Adatsorok egyenletes ritkítására is felhasználható az awk. Az alábbi programot végrehajtva az awk minden 4. sort ír a kimenetre:

 awk '{if (count++%4==0) print $0;}' adatsorom.csv

vagy egyszerűbben:

 awk '!(count++%4)' adatsorom.csv

Sorok, szavak és karakterek megszámlálása 
Az alábbi awk program megszámolja, majd kiírja a bemeneti fájlban lévő sorok, szavak és karakterek számát úgy, mint a wc nevű program:
# cnt.awk -- szamlalo
 {
     w += NF
     c += length + 1
 }
 END { print NR, w, c }

Néhány sortól terjedelmesebb awk programokat szokás szövegfájlba (szkript) menteni. Futtatásuk a következő módon is lehetséges:

 awk -f cnt.awk < valami.txt

Hatására az interpreter beolvassa az -f kapcsoló után álló szkriptet (cnt.awk), majd feldolgozza a valami.txt nevű szövegfájlt a szkript szabályai szerint. Az eredményeket (szavak számát) a képernyőre írja (stdout).

Szavak előfordulási gyakoriságának kiszámítása 
Szavak gyakoriságáról készít statisztikát asszociatív tömb felhasználásával a következő példa:

#stat.awk
 BEGIN { RS="[^a-zA-Z]+"} 
 
 { words[tolower($0)]++ }
 
 END { for (i in words)
     print i, words[i]
 }

A következő példa feltételes utasítást használ mintakeresésre:

 # cal.awk -- kalóriaszámláló
 # szövegfájl (táblázat) utolsó oszlopának összegzése és az összeg kiírása
 #
 # ha nincs érvényes adatpár, akkor az üres bemeneti sorokkal együtt kihagyja
 {if (NF > 1){
 # minden rekord utolsó mezőjében lévő számértéket az i-be gyűjti, hozzáadogatva
 # és kiírja az aktuális sort is
   i += $NF; print $0
   } # feltétel vége
 } # törzs vége
 # ha nincs több sor, kiírja az összeg értékét két tabulátor után
 END{print "\t\t" i} 
 
Egy awk programban tetszőleges számú megjegyzést lehet elhelyezni. A megjegyzés sorok a rács (hashmark) "#" karakterrel kezdődnek
és az aktuális programsor végéig tartanak. A megjegyzések és a strukturált írásmód segítik a programok értelmezését más olvasók számára. Előző példa írható tömörebben is:
 # cal.awk
 {if (NF > 1){i += $NF; print $0}}
 END{print "\t\t" i}

Speciális jelek keresése 
Néhány karakter az awk-nyelv részére már foglalva van, tehát nem lehet rájuk közvetlenül keresni. Így például a megjegyzés jel # közvetlenül nem tehető mintába, hiszen előfordulása arra utasítja az értelmezőt, hogy a programsor további részét hagyja figyelmen kívül! Hasonlóan nem lehet közvetlenül mintaként megadni a műveleti jeleket, operátorokat sem. A probléma feloldására
a speciális karaktereket úgynevezett védő-karakterrel (egy darab per-jellel) kell ellátni, ami arra utasítja az interpretert, hogy a következő karaktert ne a vezérlés részeként, hanem szó szerint értelmezzen. Például a megjegyzést tartalmazó minta a következők szerint kereshető:
 # megjegyzés karakter a mintában
 /\#/ 

A keresés megfordítható a negáló (!) operátor segítségével:
# ha: nem megjegyzéssel kezdődő sor, nem üres sor és van érvényes adatpár is
! /^\#/  {if (NF > 1){

Fentieken kívül még számos bonyolultabb program is készíthető awk nyelven, például LISP-interpreter, PRAG a groff részére, stb.

Jegyzetek

Kapcsolódó szócikkek 
 Rekurzió

További információk 
 A GNU Awk felhasználói kézikönyve
 Alfred V. Aho, Brian W. Kernighan, and Peter J. Weinberger: Az AWK Programozási nyelv. Addison-Wesley, 1988, 
  - az eredeti Awk
 comp.lang.awk – Usenet hírcsoport
  – GAWK (GNU Awk) Weboldal
  – AWK-bevezető németül
 The awk programming language

Scriptnyelvek