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
(see section Függvényhívások),
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é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 fut
(see section Command Line Options), 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.
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
(see section A delete
kifejezés). 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
(see section Dátumfeldolgozó függvények).
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) }
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 parancssorban
(see section Command Line Options), 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 (see section A next
kifejezés)
használod egy általad definiált függvényben. A gawk
-nak
nincs ilyen problémája.
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.
Go to the first, previous, next, last section, table of contents.