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.