@documentlanguage hu
"To boldly go where no man has gone before" a
Paramount Pictures Corporation regisztrált védjegye.
Copyright (C) 1989, 1991, 92, 93, 96, 97, 98, 99 Free Software Foundation, Inc.
A Hatékony AWK programozás 1.0.4 kiadása
a 3.0.4 (vagy későbbi) GNU AWK implementációt mutatja be.
Kiadta:
sales@ssc.com gnu@gnu.org
http://www.ssc.com/ http://www.fsf.org/
ISBN 1-882114-26-4
Ez a könyv az awk nyelvet mutatja be, és hogy hogyan lehet azt
hatékonyan használni. Már ismerned kell néhány alapvető rendszerparancsot,
úgy mint a cat, az ls(1),
illetve tisztában kell legyél a shell lehetőségeivel, például
bemenet/kimenet (I/O) átirányítás fogalmával és a csövek (pipe) használatával.
Az awk nyelv különböző implementációja sokféle számítógépen elérhető.
Ez a könyv, bár az awk nyelv általános tulajdonságait is elmagyarázza,
az awk egy speciális implementációját mutatja be, a gawk-ot
("GNU awk"). A gawk sokféle Unix rendszeren fut, kezdve a PC-ktől
a Cray számítógépekig. A gawk futtatható MS-DOS, OS/2 PC, Atari, Amiga
és VMS operációs rendszereken is.
awk és a gawk története
Az awk név az alkotók kezdőbetűiből áll össze:
Alfred V. Aho, Peter J. Weinberger, and Brian W. Kernighan. Az eredeti
awk 1977-ben az AT&T Bell laboratóriumában készült. 1985-ben
egy új verzió továbbfejlesztette a nyelvet, bevezette a felhasználó
által definiálható függvényeket, több bemeneti folyamot (stream)
és a dinamikus reguláris kifejezéseket. Ezt a verziót használta a Unix
System V Release 3.1. A System V Release 4 -el érkező újabb verzió
további lehetőségeket adott a nyelvhez, és le is tisztította a
nyelv "sötét sarkait". A "POSIX Command Language and Utilities" (POSIX
parancsnyelv és segédprogram) szabvány letisztázta a nyelv alapjait az
eredeti (Bell laboratórium) awk és a gawk tervezőinek
segítségével.
A GNU implementációt, a gawk-ot, 1986-ban Paul Rubin és Jay Fenlason írta
Richard Stallman tanácsai alapján. John Woods írta a kód egy részét.
1988-ban és 1989-ben David Trueman, Arnold Robbins segítségével, alaposan
átdolgozta a gawk-ot, hogy kompatíbilis legyen az újabb
awk verziókkal. A jelenlegi fejlesztés a hibák
kijavítására, a teljesítmény javítására,
a szabvánnyal való kompatíbilitás biztosítására és néha
új lehetőségek kipróbálására koncentrál.
A Szabad Szoftver Alapítvány (Free Software Foundation - FSF) egy
nonprofit szervezet, amely a szabadon terjeszthető szoftverek
készítésére és terjesztésére jött létre. Richard M.
Stallman, az eredeti Emacs editor írója, alapította. A GNU
Emacs a legelterjedtebb Emacs verzió.
A GNU project a Szabad Szoftver Alapítvány folyamatos
erőfeszítése, hogy teljes, szabadon terjeszthető és POSIX
kompatíbilis környezetet hozzon létre. (A GNU jelentése: "GNU's not
Unix".) Az FSF a "GNU General Public License" (vagy GPL) licencet
használja annak biztosítására, hogy a szoftverei
forráskódja mindig hozzáférhető legyen a felhasználók
számára. A GPL egy példányát megtalálhatod a könyv végén
(see section GNU GENERAL PUBLIC LICENSE). A GPL a gawk C
forráskódjára vonatkozik.
Egy parancsértelmező (shell), egy editor (Emacs), hordozható, optimalizáló C, C++ és
Objective-C fordítók (compiler), egy debugger és több tucat nagyobb és kisebb
segédprogram (mint a gawk) a gyümölcsei a project-nek, és mind
szabadon hozzáférhető. E dokumentum irásának pillanatában (1997 eleje)
a GNU operációs rendszer kernele (a HURD), bár hozzáférhető, de a fejlesztés
egy korai stádiumában van.
Amíg a GNU operációs rendszer el nem készül, a Linux rendszert érdemes
használni, ami egy szabadon hozzáférhető, Unix jellegű operációs rendszer
80386, DEC Alpha, Sun SPARC és más rendszerekre. Sok könyvet írtak a Linuxról,
az egyik szabadon elérhető a Linux Installation and Getting Started
Matt Welsh által. Többféle Linux disztribúció elérhető, gyakran
számítógépes boltokban vagy egy könyv CD mellékleteként.
(További három, szabadon terjeszthető Unix jellegű operációs rendszer
létezik, a NetBSD, a FreeBSD és az OpenBSD. Ezek a
4.4-Lite Berkeley Software Distribution -on alapulnak és a gawk-ot
használják mint awk.)
Ez a könyv, amit éppen olvasol, szintén ingyenes. A könyvbeli
információ szabadon hozzáférhető bárki számára, a könyv forrása
a gawk-al egy közös csomagban található. Bárki lemásolhatja
ezt a könyvet, annyiszor ahányszor csak akarja. (Szentelj
egy percet a másolási engedély elolvasásának a "Copyright" oldalon.)
Ha pénzt fizettél ezért a könyvért, akkor amiért igazából fizettél
az a könyv nyomtatása és kötése és a kiadó esetleges extra
költségei. Mindent megtettünk, hogy ez a költség lehetőleg alacsony
legyen; az emberek egy jelentős része a kötött könyvet
részesíti előnyben, mint egy 330 oldalból álló
fénymásolt, valamilyen módon összekötött anyagot (nem is
beszélve a másolási időről és munkáról). Ugyanez igaz
amikor a könyvet a forrásból gyártod le; a kötött könyv
ára csak egy kicsit több mint ha magad nyomtatnád ki a teljes
könyvet. @end iftex
Maga a könyv több előzetes kiadást élt meg.
A GAWK kézikönyv egy piszkozatán kezdtem el dolgozni másokkal együtt,
Diane Close, Paul Rubin és Richard Stallman, 1988 végén. Kb. 90 oldal
volt és alig mutatta be az eredeti, "régi" awk verziót. Többszöri
átdolgozás után A GAWK kézikönyv első változata 1989 oktoberében
jelent meg, mint 0.11 Beta kiadás. További jelentős változtatások
után a 0.13-as kiadás 1991 decemberében jelent meg.
David Trueman, Pat Rankin és Michal Jaegermann írta a 0.13 -as kiadás
egyes fejezeteit. Ezt az anyagot adta ki az FSF, mint kötött könyvet
1992-ben. Ezután több kisebb átdolgozás következett, úgy mint
a 0.14-es kiadás 1992 novemberében, amit az FSF 1993 januárban adott ki,
és a 0.16 -os kiadás 1993 augusztusában.
A Hatékony AWK programozás 1.0-ás kiadása A GAWK kézikönyv egy jelentős átdolgozása. Az FSF hozzájárult ahhoz, hogy most már én vagyok az elsődleges szerzője a könyvnek. Úgy éreztem, hogy találóbb cím is kellene ennek az új könyvnek.
A Hatékony AWK programozás feltétlenül fog még fejlődni. Egy elektronikus
formája a gawk disztribúcióban is megtalálható. Ha hibát találsz
a könyvben, kérlek jelentsd azt!
See section Reporting Problems and Bugs, hogy hogyan lehet
a problémákat jelenteni elektronikus formában, vagy írj nekem az FSF
címére.
Elismerésemet szeretném kifejezni Richard M. Stallman-nak, egy jobb világ víziójáért és bátorságáért, hogy megalapította az FSF-t és elindította a GNU project-et.
A GAWK kézikönyv eredeti tervezete az alábbi köszönetnyilvánítást tartalmazta:
Sok embernek jár köszönet azért, hogy ennek a kézikönyvnek az elkészítésében közreműködött. Jay Fenlason sok ötletet adott és mintaprogramokat írt. Richard Mlynarik és Robert Chassell megjegyzésekkel segítette a kézikönyv létrejöttét. John W.Pierce (UC San Diego, Kémia tanszék) A Supplemental Document for
awkcímű cikke pontosan bemutatta ennek a dokumentumnak és azawknyelvnek azon hiányosságait, ami egyébként elkerülte volna a figyelmünket.
Az alábbi emberek hasznos tanácsokat adtak a A GAWK kézikönyv 0.13-as kiadásához: Rick Adams, Michael Brennan, Rich Burridge, Diane Close, Christopher ("Topher") Eliot, Michael Lijewski, Pat Rankin, Miriam Robbins és Michal Jaegermann.
Az alábbi emberek hasznos tanácsokat adtak a Hatékony AWK programozás 1.0-ás kiadásához: Karl Berry, Michael Brennan, Darrel Hankerson, Michal Jaegermann, Michael Lijewski és Miriam Robbins. Pat Rankin, Michal Jaegermann, Darrel Hankerson és Scott Deifik az 1.0-ás kiadás bekezdéseit frissítették.
Robert J. Chassell a Texinfo használatában nyújtott segítséget. Azért külön köszönet illeti, hogy nem engedte, hogy a könyv címe a Hogyan Gawk-oljunk illedelmesen legyen. Karl Berry a Texinfo TeX részében segített sokat.
David Trueman-nak külön köszönet jár, mivel értékes segítséget nyújtott
a gawk fejlesztésében, hogy az mindig kellően gyors legyen,
és minél kevesebb hiba legyen a kódban. Bár ma már nem vesz részt a
gawk fejlesztésében, nagy öröm volt vele dolgozni ezen a
project-en.
Scott Deifik, Darrel Hankerson, Kai Uwe Rommel, Pat Rankin és Michal
Jaegermann (nem fontossági sorrendben) a gawk hordozhatóságáért
felelős csoport tagjai már hosszú ideje. Munkájuk és segítségük nélkül
a gawk közel sem lenne olyan jó program, mint amilyen ma. Mindig
is öröm volt és ma is az, hogy velük dolgozhatok.
Jeffrey Friedl felbecsülhetetlenül értékes segítséget nyújtott néhány,
a gawk 3.0-ás verziójának kibocsátása előtti, a reguláris kifejezésekkel
kapcsolatos hiba megtalálásában.
David és én meg szeretnénk köszönni Brian Kernighan (Bell laboratórium)
felbecsülhetetlenül értékes segítségét a gawk tesztelése során,
ill. hogy segített megérteni több problémás kérdést a nyelvvel kapcsolatban.
Sem a gawk, sem a dokumentációja nem lenne olyan jó mint most a
segítsége nélkül.
Meg szeretném köszönni Marshall és Elaine Hartholz-nak (Seattle) és Dr.
Bert és Rita Schreiber-nek (Detroit) a hosszú és nyugodt vakációkat, amit
otthonukban töltöttem, így jelentősen előre tudtam haladni a gawk
és e könyv elkészítésében. Phil Hughes (SSC) szintén jelentősen
hozzásegített a cél eléréséhez, mivel kölcsönadta a Linux laptop-ját, nem is
egyszer, de kétszer, így lehetővé tette számomra, hogy az otthonomtól távol
is dolgozhassam.
Legvégül, megköszönöm feleségemnek, Miriam, a türelmét e project többszöri
átdolgozása során, azt hogy elsőként átolvasta és javította hibákat, illetve
hogy megosztott velem egy számítógépet. Meg szeretném köszönni szüleimnek
a szeretetüket és a jóindulatukat amivel felneveltek.
Ezenkívül hálával gondolok Istenre a sok lehetőségért amit számomra nyújtott,
és azokért a nekem adott "ajándékokért" amivel e lehetőségeket fel tudtam
használni.
Arnold Robbins
Atlanta, Georgia
February, 1997
Ha Te is olyan felhasználó vagy, mint megannyi más computer
felhasználó, akkor gyakran szeretnél módosítani szöveg file-okat,
amikben bizonyos minták sűrűn fordulnak elő, vagy bizonyos
sorokból adatot szeretnél kinyerni, és minden mást eldobni. Egy C
vagy Pascal proramot írni ilyen feladatra időt rabló és
kényelmetlen, ill. több sornyi kódot igényel. Valószínűleg
könnyebben megoldható a feladat awk-al.
Az awk program egy olyan speciális célú programnyelvet
képes értelmezni,
(mint egy interpreter - a fordító), amely lehetővé tesz
egyszerű adatformázási munkákat néhány sor kóddal.
Az awk GNU megvalósítását gawk-nak hívják;
teljesen kompatíbilis az awk System V Release 4 verziójával.
Ezenkívül a gawk teljesen kompatíbilis az
awk programozási nyelv POSIX szabványban rögzített
definíciójával. Ez azt jelenti, hogy minden jól megírt
awk programnak működnie kell a gawk-al is. Így nincs
értelme különbséget tenni a gawk és az awk
implementációja között.
Az awk kifejezés vonatkozik magára a programra és a programozási
nyelvre is. A könyv során ha szükséges a megkülönböztetés, akkor a
programot "az awk segédprogram" és a nyelvet "az awk
programozási nyelv" kifejezéssel jelöljük. A gawk kifejezés
a GNU "project" részeként kifejlesztett awk verziót jelöli.
A könyv célja, hogy bemutassa az awk programozási nyelvet és
az awk program futtatási lehetőségeit.
A könyv fő célja bemutatni az awk programozási nyelv
POSIX szabványban definiált tulajdonságait egy konkrét implementáción,
a gawk-on, keresztül. Ugyanakkor megpróbáljuk felhívni a
figyelmet a gawk és más awk megvalósítások közötti
fontos különbségekre, illetve minden a gawk által
implementált, de a POSIX szabványban nem rögzített lehetőségre is.
A könyv egyszerre próbál felhasználói és referencia kézikönyvként szolgálni, ezért ha kezdő felhasználó vagy, nyugodtan ugord át a bonyolultabb részeket. A sok kereszthivatkozás sem fontos most számodra, mivel ezek a profi felhasználók számára hasznosak, és a könyv "on-line" verziójához szükségesek.
Az awk program kifejezés az általad írt programra vonatkozik,
amit az awk programozási nyelven írtál.
See section Kezdő lépések awk-ban,
ami elmagyarázza az abszolút minimum és szükséges
tudást az awk használatához.
Néhány hasznos "egysoros" program található
a könyv következő
fejezetében, (see section Hasznos egysoros programok),
hogy ráérezz az awk nyelvre.
További awk példaprogramokat találsz az alábbi fejezetekben.
(See section A Library of awk Functions, illetve
see section Practical awk Programs).
A awk programozási nyelv összegzését a gyors keresés
érdekében megtalálhatod a függelékben, section gawk Summary.
Ha csak a memoriádat akarod felfrissíteni az awk
nyelv egy adott részleteről, akkor ezt a fejezetet neked találták ki.
Ha ismeretlen kifejezéssel találkozol, üsd fel a (see section Glossary) fejezetet.
A legtöbb esetben teljes awk programot adunk meg mint példát,
de a haladó fejezetekben előfordul, hogy csak egy programrészletet használunk a koncepció bemutatására.
A könyv főleg olyan felhasználókat céloz meg, akik még nem ismerik az
awk programozási nyelvet, ugyanakkor sok információt tartogat
a profi felhasználók számára is. Ilyen például az awk POSIX
definíciója, és a példaprogramok jelentős része a
section A Library of awk Functions, és
section Practical awk Programsán.
Ki nyitotta ki azt az elsötétített ablakot?!? Gróf Dracula
A POSIX szabvány (és a The Gawk Manual) megjelenéséig
az awk sok tulajdonsága gyengén vagy egyáltalán nem volt
dokumentálva. Ezeket hívják "sötét sarkoknak".
A könyvben az "(s.s.)" jelzéssel hívjuk fel rájuk a figyelmet, és
a tárgymutatóban is megtalálhatóak a "sötet sarkok" címszó alatt.
Ez a könyv GNU Texinfo formátumot használja a szöveg szedésére. A Texinfo file-ból el lehet készíteni a dokumentum nyomtatott vagy "on-line" verzióját. Ebből kifolyólag a szedési minta eltér más, általad olvasott könyvekétől.
Az általad begépelhető parancssorokat megelőzi a "shell" elsődleges és másodlagos "prompt"-ja, `$' és `>'. A parancs kimenetét, eredményét a "-|" jel előzi meg. Ez általában a parancs szabványos kimenetét jelöli. A hibaüzenetek és egyéb üzenetek előtt az "error-->" jel lesz feltüntetve. Például:
$ echo hi on stdout -| hi on stdout $ echo hello on stderr 1>&2 error--> hello on stderr
A szövegben a parancsnevek ezzel a karakterrel jelennek meg.
A kódrészleteket ugyanezzel a betűvel írjuk, de
idézőjelek között,
`például így'. Néhány dolgot hangsúlyozni
szeretnénk az előbbi
módon, a különösen fontosakat pedig így fogjuk írni.
Egy új kifejezés első előfordulásánál általában
a definícióját is megadjuk, és ez olyan
betűtípussal fog megjelenni,
mint amit a "definíció" szónal használtunk ebben a mondatban.
A file-nevek pedig így fognak kinézni: `/path/to/ourfile'.
Az általad begépelt betűk szedése ilyen lesz. Van néhány speciális karakter, a "vezérlő karakterek", amelyeket úgy tudsz begépelni, hogy lenyomva tartod a CONTROL billentyűt, amíg megnyomod a kívánt, másik billentyűt. Például a Control-d esetén először megnyomod és lenyomva tartod a CONTROL billentyűt, majd megnyomod a d billentyűt. Végül mindkét billentyűt felengeded.
A példák jelentős része két minta adat file-t használ. Az első file, `BBS-list', néhány elektronikus faliújságként szolgáló rendszer listája. A második file, `inventory-shipped', néhány szállítmány listáját tartalmazza havi bontásban. Mindkét file-ban, minden egyes sort egy rekordnak tekintünk.
A `BBS-list' file-ban, minden rekord tartalmazza a számítógép nevét, telefonszámát, az átviteli sebességét (baud) és egy kódot ami megmondja, hogy a gép hány órát van üzemben. Az `A' kód az utolsó oszlopban azt jelenti, hogy a faliújság napi 24 órát van üzemben. A `B' kódú számítógépek csak este és a hétvégi órákban üzemelnek. A `C' kód jelentése pedig, hogy a faliújság csak a hétvégén elérhető.
aardvark 555-5553 1200/300 B alpo-net 555-3412 2400/1200/300 A barfly 555-7685 1200/300 A bites 555-1675 2400/1200/300 A camelot 555-0542 300 C core 555-2912 1200/300 C fooey 555-1234 2400/1200/300 B foot 555-6699 1200/300 B macfoo 555-6480 1200/300 A sdace 555-3430 2400/1200/300 A sabafoo 555-2127 1200/300 C
A második file, `inventory-shipped', az egy év során teljesített szállítmányok listáját tartalmazza. Minden rekord tartalmazza a hónapot, a zöld rekeszek, a piros dobozok, a narancsos zsákok és a kék csomagok számát. A file 16 bejegyzése magába foglalja egy év 12 hónapját és a következő év első négy hónapját.
Jan 13 25 15 115 Feb 15 32 24 226 Mar 15 24 34 228 Apr 31 52 63 420 May 16 34 29 208 Jun 31 42 75 492 Jul 24 34 67 436 Aug 15 34 47 316 Sep 13 55 37 277 Oct 29 54 68 525 Nov 20 87 82 577 Dec 17 35 61 401 Jan 21 36 64 620 Feb 26 58 80 652 Mar 24 75 70 495 Apr 21 70 74 514
awk-ban
Az awk alapvető feladata, hogy olyan sorokat (vagy más szövegegységeket)
keressen file-okban, amelyek tartalmaznak egy bizonyos mintát. Amikor egy
sor illeszkedik egy mintára, az awk segédprogram egy megadott
feladatot vagy tevékenységet végez el a soron. Ezt addig ismétli minden soron,
amíg a file-ok végére nem ér.
Az awk-ban írt programok különböznek a más programozási nyelven írt
programoktól, mert az awk programok adatvezéreltek; ez azt
jelenti, hogy először azt az adatot adod meg amivel dolgozni szeretnél, és
csak utána adod meg, hogy mit szeretnél az adattal csinálni, ha megtaláltad.
A legtöbb programozási nyelv procedurális; apró részletekre lebontva
meg kell adnod a programnak, hogy mit csináljon. Amikor procedurális
programozási nyelvvel dolgozol, általában nehéz pontosan megadni, hogy a
programod milyen adatot fog feldolgozni. Mivel ez az awk-ban nem így
van, ezért fogod könnyűnek találni az awk programok írását és
olvasását.
Az awk segédprogram futásához meg kell adnod egy awk
programot, ami definiálja az awk számára hogy mit kell
csinálnia. A program szabályokat tartalmaz. (Tartalmazhat függvény
definíciót is, ami már bonyolultabb, és így most nem mélyedünk el benne,
see section Felhasználó által definiált függvények.)
Minden szabály tartalmaz egy mintát, amit keresünk, és egy tevékenységet
amit végre kell hajtani, ha a mintát megtalálta a program.
Szintaktikailag egy szabály egy mintából, majd egy azt követő tevékenységből
áll. A tevékenységet kapcsos zárójelek közé kell tenni, hogy elválasszuk a
mintától. A szabályok általában egy új sorral vannak elválasztva. Egy
awk program így néz ki:
minta { tevékenység }
minta { tevékenység }
...
Az awk programozási nyelv hosszú évek során fejlődött. Erről további
részletek találhatók egy későbbi fejezetben,
section The Evolution of the awk Language.
Az ebben a könyvben bemutatott nyelvet gyakran hívják az "új awk"-nak.
Ennek következtében sok rendszer többféle awk verziót tartalmaz. Néhány
rendszer rendelkezik egy awk segédprogrammal, ami az eredeti awk
implementációja, és egy nawk programmal, ami az új verzió. Más
rendszereken oawk-nak hívják a "régebbi awk"
implementációt, és az awk nevet használják az új verzióra. Előfordul, hogy
csak egy verzió található meg a rendszeren, általában az újabb. (2)
Ebben a helyzetben bonyolult lehet azt eldönteni, hogy melyik
awk verziót használd amikor a programot írod. A legjobb
tanács, amit adhatunk, hogy olvasd el a rendszereden levő dokumentációt.
Ellenőrizd, hogy awk, oawk, nawk vagy gawk
program létezik-e a rendszereden. Jó esélyed van arra, hogy az új verzió
valamilyen implementációját megtalálod a gépeden, és azt tanácsoljuk, hogy
ezt használd. (Természetesen, ha ezt a könyvet olvasod, nagyon valószínű,
hogy a gawk implementációt megtalálod a rendszereden!)
Amikor olyan részletre hivatkozunk aminek bármely POSIX
kompatíbilis implementációban szerepelnie kell, az awk nevet fogjuk
használni, csak a GNU implementációra jellemző részeknél használjuk a gawk
nevet.
awk programokat
Sokféle lehetőség van egy awk program futtatására. Ha a program rövid,
a legegyszerűbb a parancssorban elhelyezni, például:
awk 'program' input-file1 input-file2 ...
ahol a program mintákat és tevékenységeket tartalmaz, ahogy korábban
elmagyaráztuk. (Miért használunk idézőjeleket? A magyarázatot
megtalálod alább, section Egyszer használatos, eldobható awk programok.)
Amikor egy program hosszabb, kényelmesebb egy file-ban elhelyezni és így futtatni:
awk -f program-file input-file1 input-file2 ...
awk programok
Ha már jobban megismerted az awk programozási nyelvet, gyakran
fog előfordulni, hogy az egyszerűbb programokat azonnal megírod
a terminál előtt ülve, amint használni szeretnéd. Ebben az esetben az
awk parancs a:
awk 'program' input-file1 input-file2 ...
ahol a program mintákat és tevékenységeket tartalmaz.
Ez a parancsformátum arra utasítja a shell-t, vagy a parancs értelmezőt,
hogy indítsa el az awk segédprogramot, ami majd használni fogja a
programot a bemeneti file-ok tartalmának feldolgozására. Az idézőjelek
azért vannak a program körül, hogy a shell ne értelmezze ezt a részt.
Ráadásul, az idézőjelek arra is utasítják a shell-t hogy az egészet egyetlen
argumentumként adja át az awk-nak, így lehetővé téve több sorból álló
program megadását is.
Ez a forma lényegében megfelel közepes méretű awk programok
shell script-ből való futtatására is, mivel így elkerülhető a külön file
használata az awk programhoz. Az egy file-ból álló programok
megbízhatóbbak, mivel nincs másik file, amire szükség volna, és esetleg rossz
helyen lenne.
A section Hasznos egysoros programok, bemutat néhány rövid, egyszerű programot.
Mint érdekesség, megjegyezzük, hogy az
awk '/foo/' files ...
parancs lényegében ugyanaz mint a
egrep foo files ...
awk futtatása bemeneti file-ok nélkül
Az awk segédprogram bemeneti file-ok nélkül is futtatható. Ha
begépeled:
awk 'program'
akkor az awk programot a szabványos bemenetre
fogja alkalmazni, ami általában az a szöveg, amit a terminálon begépelsz.
Ez egészen addig tart amíg be nem gépeled hogy Control-d, vagyis
jelzed hogy a "file"-nak vége. (Más operációs rendszereken a file
vége jel más lehet, pl. OS/2 és MS-DOS alatt a Control-z.)
Például az alábbi program kinyomtat egy barátságos tanácsot, (ollózva Douglas Adams művéből, Galaxis útikönyv stopposoknak), aminek segítségével elkerülheted a számítógép által okozott fejfájást. (`BEGIN'-t még eddig nem magyaráztuk el)
$ awk "BEGIN { print \"Don't Panic!\" }"
-| Don't Panic!
Ez a program nem olvas be semmit. A `\' jel minden belső macskaköröm előtt szükséges a shell miatt, különösen mivel idézőjelet és macskakörmöt kevertünk a szövegben.
A következő egyszerű awk program a cat segédprogramot
emulálja; amit begépelsz, kinyomtatja. (Hogy ez miért működik, azt hamarosan
eláruljuk.)
$ awk '{ print }'
Now is the time for all good men
-| Now is the time for all good men
to come to the aid of their country.
-| to come to the aid of their country.
Four score and seven years ago, ...
-| Four score and seven years ago, ...
What, me worry?
-| What, me worry?
Control-d
Előfordul, hogy az awk programod hosszú lesz. Ebben az esetben
kényelmesebb lehet a programot egy külön file-ba tenni. Az awk
parancs ennek a file-nek a futtatására:
awk -f source-file input-file1 input-file2 ...
A `-f' argumentum adja meg az awk-nak, hogy a végrehajtandó program
a source-file-ban van. A source-file-nak bármilyen neve lehet.
Például az alábbi programot
BEGIN { print "Don't Panic!" }
beteheted az `advice' file-ba. A parancs:
awk -f advice
ugyanazt fogja csinálni, mint a :
awk "BEGIN { print \"Don't Panic!\" }"
amit már korábban elmagyaráztunk.
(See section Az awk futtatása bemeneti file-ok nélkül.)
Érdemes megjegyezni, hogy a file-név köré, amit a `-f' argumentummal
definiálsz, nem kellenek idézőjelek, mivel a file-nevek általában nem
tartalmaznak speciális shell karaktereket. Természetesen az idézőjelek az
`advice' file-on belül sem kellenek az awk program köré.
Az idézőjelek csak a parancssorban megadott awk programok esetén
kellenek.
Ha azonosítani szeretnéd awk programjaidat, akkor használhatod
a `.awk' kiterjesztést a file név után. Ez nem befolyásolja az
awk program működését, de egyszerűbbé teheti file-jaid
nyilvántartását.
awk programok
Miután megtanultad az awk nyelvet, önmagában futtatható programokat
is írhatsz a `#!' mechanizmus segítségével. A legtöbb Unix rendszeren
(3) ez működni fog (és valamikor
a GNU rendszeren is.)
Például kiegészítheted az `advice' file-t az alábbiak szerint:
#! /bin/awk -f
BEGIN { print "Don't Panic!" }
Miután a file-t futtathatóvá tetted (a chmod segédprogrammal),
ha egyszerűen begépeled a shell-nek, hogy `advice', a rendszer úgy
állít be mindent, mintha az awk(4)
segédprogramot a `awk -f advice' paranccsal futtatnád.
$ advice -| Don't Panic!
Az önmagában futtatható awk programok akkor is jó szolgálatot tehetnek,
ha olyan programot szeretnél írni, amit más felhasználók is futtathatnak
anélkül, hogy ismernék az awk-ot.
Figyelem: Ne tegyél egynél több argumentumot a `#!' jellel kezdődő
sorba az awk után. Ez nem működik. Az operációs rendszer az
interpreter utáni részt úgy
kezeli, mint egyetlen argumentum, és így adja át az awk-nak. Következésképpen
furcsa dolgok történhetnek: a legvalószínűbb, hogy a program használatáról kapsz
valamilyen ismertetést.
Néhány régebbi rendszer nem támogatja a `#!' mechanizmust. Ugyanazt a hatást érheted el egy shell script-el, aminek valahogy így kell kinéznie:
: Ezt biztosítja, hogy az alap shell fogja végrehajtani. awk 'program' "$@"
Ebben az esetben alapvető fontosságú, hogy a programot idézőjelek közé tedd, így a shell nem fogja értelmezni. Ha mégis lehagynád, csak egy shell varazsló a megmondhatója, hogy mi fog történni.
A "$@" jel hatására a shell minden, a parancssorban megadott
argumentumot átad az awk segédprogramnak, anélkül hogy azokat értelmezné.
Az első sorban elhelyezett kettőspont segítségével a program még C shell-t
használó embereknek is működni fog. (Nem minden régebbi rendszer ismeri ezt a
módszert, de a legtöbb igen.)
awk programokbanEgy megjegyzés lényegében egy szöveg, ami az emberek számára került be a programba, és nem igazán része a programnak. A megjegyzések például elmagyarázhatják, hogy a program mit csinál, és hogyan működik. Szinte minden programozási nyelvben lehet megjegyzéseket elhelyezni, mivel a programokat általában nehezebb megérteni a megjegyzések által adott extra segítség nélkül.
Az awk programozási nyelvben a megjegyzések egy `#' karakterrel
kezdődnek és a sor végéig tartanak. A `#' karakternek nem kell a sor
első karakterének lennie. Az awk nyelv a karakter utáni teljes sort
eldobja. Például az alábbiakkal egészíthetjük ki az `advice' programot:
# Ez a program egy barátságos üzenetet nyomtat ki. Így
# segít abban, hogy ne félj az új dolgoktól.
BEGIN { print "Don't Panic!" }
Ha akarsz, tehetsz megjegyzéseket az egyszer használatos programokba is, de ez nem túl hasznos; a megjegyzések fő célja, hogy segítsen téged vagy más felhasználókat a program megértésében egy későbbi időpontban.
Figyelem: Ahogy azt már korábban megemlítettük,
section Egyszer használatos, eldobható awk programok, a kicsi és közepes
programokat idézőjelek közé teheted, hogy önmagukban is futtatható shell
script-ek legyenek. Ha ezt a módszert választod ne használj idézőjelet
a megjegyzésekben (sehol a programodban). A shell ezt úgy értelmezné mint a
lezárását a kezdő idézőjelnek. Az eredmény? A shell jelezni fogja hogy nincs
azonos számú kezdő és záró idézőjel, és ha ezután az awk mégis
elindulna, akkor valószínűleg hibaüzeneteket fog kiírni. Például:
awk 'BEGIN { print "hello" } # let's be cute'
Az alábbi parancs egy egyszerű awk programot futtat le, ami a
`foo' szó minden előfordulását megkeresi a `BBS-list' file-ban.
awk '/foo/ { print $0 }' BBS-list
Amikor az awk olyan sort talál, ami tartalmazza a `foo' szót,
kinyomtatja az adott sort, mivel a `print $0' ezt jelenti:
nyomtasd ki az aktuális sort. (Ugyanez érhető el az egyszerű
`print' paranccsal, így az is használható.)
A példában `/' jelek veszik körül a `foo' szót az awk
programban. Ezek a jelek határozzák meg, hogy a `foo' mintát szeretnénk
megtalálni. Az ilyen mintákat reguláris kifejezésnek nevezzük és később
tárgyaljuk részletesen (see section Reguláris kifejezések).
Lehet, hogy a minta a szónak csak egy részére illeszkedik.
Az awk program körüli idézőjelek hatására a shell semelyik
speciális shell karaktert (ami esetleg előfordulhat a mintában) nem fogja
értelmezni.
A program eredménye:
$ awk '/foo/ { print $0 }' BBS-list
-| fooey 555-1234 2400/1200/300 B
-| foot 555-6699 1200/300 B
-| macfoo 555-6480 1200/300 A
-| sabafoo 555-2127 1200/300 C
Egy awk szabályban vagy a minta vagy a tevékenység elhagyható, de
egyszerre mindkettő nem. Ha a minta nincs megadva, akkor az adott tevékenység
minden sorra végrehajtódik. Ha a tevékenység lett elhagyva, akkor az alaptevékenység hajtódik végre, kinyomtat minden olyan sort, amire a minta
illeszkedik.
Ezért van az, hogy a fenti példában elhagyhatjuk a tevékenységet
(a print és a kapcsos zárójeleket) és az eredmény ugyanaz lesz:
minden olyan sort kinyomtat, ami illeszkedik a `foo' mintára.
Összehasonlításképpen, ha a print tevékenységet elhagyjuk, de a
kapcsos zárójeleket megtartjuk, ami egy üres tevékenységet jelent,
akkor egyetlen sort sem fog a program kinyomtatni.
Az awk segédprogram a bemeneti file-okat olvassa soronként és
megpróbálja illeszteni a mintákat minden egyes sorra. Ha több minta is
illeszkedik, akkor több tevékenység hajtodik végre, abban a sorrendben, ahogy
az awk programban definiálva lettek. Ha egyetlen minta sem illeszkedik,
akkor nem történik semmi.
Miután az aktuális sorra illeszkedő szabályokat
feldolgozta (lehet hogy egyet sem),
az awk beolvassa a következő sort (de érdemes figyelembe venni a
section A next kifejezés,
és a section A nextfile kifejezés által leírtakat).
Ezt a folyamatot a file végéig ismétli.
Például az alábbi awk program:
/12/ { print $0 }
/21/ { print $0 }
két szabályt tartalmaz. Az első szabályban a minta az `12' szó, és a `print $0' a tevékenység. A második szabály mintája a `21' szó, és a tevékenység szintén a `print $0'. Mindegyik szabály körül kapcsos zárójelek vannak, külön-külön.
Ez az awk program kinyomtat minden olyan sort, amelyik vagy az
`12' vagy a `21' szót tartalmazza. Ha egy sor mindkét szót
tartalmazza, akkor kétszer lesz kinyomtatva, mindegyik szabály által egyszer.
Ha a fenti programot a két mintafile-lal (`BBS-list' és `inventory-shipped') futtatjuk, akkor az alábbi eredményt kapjuk:
$ awk '/12/ { print $0 }
> /21/ { print $0 }' BBS-list inventory-shipped
-| aardvark 555-5553 1200/300 B
-| alpo-net 555-3412 2400/1200/300 A
-| barfly 555-7685 1200/300 A
-| bites 555-1675 2400/1200/300 A
-| core 555-2912 1200/300 C
-| fooey 555-1234 2400/1200/300 B
-| foot 555-6699 1200/300 B
-| macfoo 555-6480 1200/300 A
-| sdace 555-3430 2400/1200/300 A
-| sabafoo 555-2127 1200/300 C
-| sabafoo 555-2127 1200/300 C
-| Jan 21 36 64 620
-| Apr 21 70 74 514
Érdemes megfigyelni, hogy a `sabafoo' kezdetű sor a `BBS-list' file-ból kétszer lett kinyomtatva, mindegyik szabály kinyomtatta egyszer.
A következő példa talán jobban bemutatja, hogy az awk mire is képes,
úgy mint összegzésre, kiválasztásra és egy másik segédprogram kimenetének
újrarendezésére. A példa tartalmaz olyan programozási megoldásokat, amiket
eddig nem tárgyaltunk, így ne aggódj, ha nem érted minden részletét.
ls -lg | awk '$6 == "Nov" { sum += $5 }
END { print sum }'
A parancs kinyomtatja az adott könyvtárban levő és legutoljára novemberben (bármely év) módosított file-ok összegzett méretét byte-ban. (Ha C shell-t használsz, akkor az első sor végén `;\' -t kell begépelni; a POSIX szabvánnyal kompatíbilis shell-ek esetén, mint a Bourne shell vagy a GNU Bourne-Again shell, a példát begépelheted, ahogy fent látod.)
Az `ls -lg' része a példának egy rendszerparancs, ami kilistázza a file-okat a könyvtárban, a file méretét és az utolsó módosítás dátumát. Az eredmény így néz ki:
-rw-r--r-- 1 arnold user 1933 Nov 7 13:05 Makefile -rw-r--r-- 1 arnold user 10809 Nov 7 13:03 gawk.h -rw-r--r-- 1 arnold user 983 Apr 13 12:14 gawk.tab.h -rw-r--r-- 1 arnold user 31869 Jun 15 12:20 gawk.y -rw-r--r-- 1 arnold user 22414 Nov 7 13:03 gawk1.c -rw-r--r-- 1 arnold user 37455 Nov 7 13:03 gawk2.c -rw-r--r-- 1 arnold user 27511 Dec 9 13:07 gawk3.c -rw-r--r-- 1 arnold user 7989 Nov 7 13:03 gawk4.c
Az első mező tartalmazza az olvasási-írási jogokat, a második a link-ek számát a file-ra. A harmadik mező adja meg a file tulajdonosának azonosítóját. A negyedik mező azonosítja a csoportot, míg az ötödik mező tartalmazza a file méretét byte-okban. A hatodik, hetedik és nyolcadik mező az utolsó módosítás hónapját, napját és óráját adja meg. Végül a kilencedik mező a file neve.
A `$6 == "Nov"' kifejezés az awk programban egy ellenőrzés, hogy
az `ls -lg' kimenetében a hatodik mező megegyezik-e a `Nov'
szóval. Minden olyan sorra, aminél a hatodik mező megegyezik a `Nov'
szóval, a `sum += $5' tevékenység hajtódik végre. Ez hozzáadja az
ötödik mező tartalmát a sum változóhoz. Miután az awk befejezte
a bemenet olvasását, a sum változó fogja tartalmazni azon file-ok méretének
összegét, amelyeknél illeszkedést talált. (Ez azért fog működni, mert
minden awk változó automatikusan zérus értéket kap kezdetben.)
Az ls-ből származó utolsó sor beolvasása után az END szabály
hajtódik végre és az awk kinyomtatja a sum változó értékét.
Ebben a példában a sum változó értéke 80600 lesz.
További bonyolult awk programozási technikákat mutatunk be a későbbi
fejezetekben (see section Tevékenységek áttekintése), de mielőtt
továbblépnél, fontos megérteni, hogy az awk hogyan dolgozza fel
a bemenetet, és jeleníti meg az eredményt. A mezők manipulálásával és a
print paranccsal több hasznos és meggyőző jelentést, összefoglalót
készíthetsz.
awk kifejezések és sorok
Általában egy sor egy kifejezést vagy szabályt tartalmaz
az awk programokban, például:
awk '/12/ { print $0 }
/21/ { print $0 }' BBS-list inventory-shipped
Ugyanakkor a gawk nem veszi figyelembe az új sort az alábbi jelek után:
, { ? : || && do else
Az új sor bármely más esetben egy kifejezés végét jelenti. (Egy új sor
kezdésének lehetősége a kérdőjel és a kettőspont után anélkül, hogy új
kifejezés kezdődne, egy gawk kiegeszítés. A kérdőjel és a kettőspont,
mint egy feltételes kifejezés jelennek itt meg, amit a
section Feltételes kifejezések fejezetben magyarázunk el.)
Ha egy kifejezést két sorban szeretnél leírni, és egy olyan pontnál új sort kezdeni, ahol alapesetben a kifejezés végét jelentené az új sor, akkor az első sor végére egy `\' jelet kell írni. A `\' jel legyen az utolsó a sorban. Ez a módszer bárhol megengedett, akár egy kifejezésben, egy szöveg közepén vagy egy reguláris kifejezésben. Például:
awk '/Ez a reguláris kifejezés túl hosszú, ezért\
ebben sorban folytatjuk./ { print $1 }'
A könyvben bemutatott példákban általában nem használjuk ezt a lehetőséget,
mivel a gawk-ban a sor hossza nem limitált; egyszerűen a programokat
teheti olvashatóbbá. Ugyanezen oknál fogva, és az érthetőség kedvéért a
példaprogramokban csak rövid kifejezéseket használtunk. A `\' jel
használata talán a leghasznosabb, ha az awk programodat egy külön file-ban
tárolod, és nem parancsként gépeled be. Fontos megjegyezni, hogy sok
awk implementáció érzékeny arra, hogy hol használjuk a `\' jelet.
Például nem engedélyezett a szövegek két sorba vágása a `\' jellel bizonyos
awk implementációkban. Ha különböző awk implementációkkal
is szeretnéd a programodat futtatni, a legegyszerűbb ha a szövegeket és
reguláris kifejezéseket nem vágod szét a `\' jellel.
Figyelem: a `\' jel fent leírt használata nem fog működni C shell-el.
A módszer csak külön file-be írt programok és a POSIX szabvánnyal
kompatíbilis shell-ek, a Bourne shell vagy a Bash, esetén működik.
De a C shell (csh) másképp viselkedik.
A C shell esetén két `\' jelet
kell használni a sor szétvágásárá, ráadásul a C shell-ben minden sort egy
`\' jellel kell lezárni, hogy a következő sorban folytatható legyen. Tehát:
% awk 'BEGIN { \
? print \\
? "hello, world" \
? }'
-| hello, world
Itt a `%' és a `?' jelek a C shell elsődleges és másodlagos "prompt"-jai, mint a `$' és a `>' jelek a szabványos shell-ek esetén.
Az awk egy sor orientált programozási nyelv. Minden szabály tevékenység
részének ugyanabban a sorban kell kezdődnie, mint a keresési minta.
Ahhoz, hogy a tevékenység és a minta külön sorba kerülhessen a `\' jelet
kell használni a fent leírt módon -- ez az egyetlen megoldási lehetőség erre.
Fontos megjegyezni, hogy a `\' jel használata és a megjegyzések nem
keverhetők. Amint egy `#' jelet beolvas az awk minden
további karaktert eldob a sorban. Például:
$ gawk 'BEGIN { print "dont panic" # a friendly \
> BEGIN rule
> }'
error--> gawk: cmd. line:2: BEGIN rule
error--> gawk: cmd. line:2: ^ parse error
Itt úgy tűnik, mintha a `\' jel megengedné a megjegyzés folytatását
a következő sorban, de a `\' jelet az awk soha nem veszi észre, mert
az a megjegyzésben "el van rejtve". Így a `BEGIN' kifejezés egy hibát
generál.
Ha egy kifejezés egy szabályon belül nagyon rövid, lehetőség van további kifejezéseknek ugyanabba a sorba helyezésére. Ezeket a kifejezéseket a `;' jellel kell elválasztani egymástól.
Ez természetesen igaz a szabályokra is, így a fenti program az alábbi formában is helyes:
/12/ { print $0 } ; /21/ { print $0 }
Megjegyzés: az egy sorba kerülő szabályok `;' jellel való elválasztása
nem része az eredeti awk nyelvnek; a kifejezések egységes kezelése érdekében
került bevezetésre.
awk sajátosságok
Az awk programozási nyelv számos olyan előre definiált vagy beépített
változó elérését biztosítja, amelyek valamilyen extra információt nyújtanak a
programnak. Más változók segítségével a program befolyásolhatja az awk
működését.
Ráadásul, az egyszerű számítások és szöveges műveletek elvégzésére számos beépített
függvény áll rendelkezésre az awk-ban.
A könyv során fokozatosan mutatjuk be a beépített változókat és függvényeket. A szisztematikus definíciójukat két fejezetben találhatod meg: section Beépített változók, section Beépített függvények.
awk-ot
Talán elgondolgoztál már azon, hogy hogyan is lehet az awk hasznos
számodra. Az egyik válasz, hogy okosan megtervezett mintákkal, mezőelválasztó
jelekkel, matematikai kifejezésekkel és más kiválasztási kritériumokkal
bonyolult összefoglalásokat, jelentéseket tudsz készíteni. Az awk nyelv
nagyon hasznos lehet nagy mennyiségű nyers adat feldolgozására, például más
segédprogramok kimenetének összegzésére
(see section Egy bonyolultabb példa).
Az awk-ban írt programok általában kisebbek, mint más programozási
nyelven írt programok. Ezért egyszerű az awk programok írása és
használata. Gyakran a leggyorsabb módszer, egyszerűen csak leülni a
terminálhoz és megírni az awk programot, majd a használat után
egyszerűen eldobni. Mivel az awk programok interpretáltak (egy
értelmező dolgozza fel és hajtja végre), elkerülhető a fordítási rész a
tipikus szerkesztés-fordítás-tesztelés-hibakeresés software fejlesztési
ciklusból.
Több bonyolult programot írtak már awk-ban, mint például egy teljes
assemblert 8 bites mikroprocesszorokra (további részletek a see section Glossary)
és egy microcode assemblert speciális Prolog számítógépek számára. Ugyanakkor
ezek az alkalmazások már megmutatják az awk korlátait is.
Ha, mondjuk, néhány száz sorból álló programokat írsz awk-ban, akkor érdemes
megfontolni egy másik programozási nyelv használatát. Az Emacs Lisp egy jó
választás ha bonyolult szöveg és minta illesztéseket kell csinálni. A shell
is alkalmas erre, ráadásul más rendszer segédprogramok is elérhetők. A C, C++
és Lisp programozási nyelvek szintén jó környezetet biztosítanak rendszer
programozáshoz és nagyobb munkák kivitelezéséhez. Az ezen a nyelveken írt programok
általában hosszabbak, mint az awk változataik, de egyszerűbb őket
karbantartani, és általában gyorsabbak.
Sok hasznos awk program rövid, csak egy vagy két sorból áll. Az
alábbiakban bemutatunk néhányat. Néhány program olyan programozási
megoldásokat is tartalmaz, amelyeket eddig nem tárgyaltunk, de a programhoz
fűzött magyarázatokkal talán érthető lesz, hogy mi is történik.
A legtöbb példa egy `data' nevű adat file-t használ, ami csak a file helyét jelöli; ha használni akarod a programokat, akkor a saját file-od nevét kell a `data' helyére írni.
awk '{ if (length($0) > max) max = length($0) }
END { print max }' data
awk 'length($0) > 80' data
expand data | awk '{ if (x < length()) x = length() }
END { print "maximum line length is " x }'
expand parancs dolgozza fel először; lecseréli
a tab karaktereket azonos számú szóközre, így amikor a sor hosszát
vizsgálja, lényegében a jobb oldali margó oszlopszámát veszi figyelembe.
awk 'NF > 0' data
awk 'BEGIN { for (i = 1; i <= 7; i++)
print int(101 * rand()) }'
ls -lg files | awk '{ x += $5 } ; END { print "total bytes: " x }'
ls -lg files | awk '{ x += $5 }
END { print "total K-bytes: " (x + 1023)/1024 }'
awk -F: '{ print $1 }' /etc/passwd | sort
awk 'END { print NR }' data
awk 'NR % 2 == 0' data
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.
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/
$ awk '$1 ~ /J/' inventory-shipped -| Jan 13 25 15 115 -| Jun 31 42 75 492 -| Jul 24 34 67 436 -| Jan 21 36 64 620Ez is ugyanazt csinálja:
awk '{ if ($1 ~ /J/) print }' inventory-shipped
exp !~ /regexp/
$ 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 ...
Amikor egy reguláris kifejezést `/' karakterek között írunk le,
általában konstans reguláris kifejezésnek hívjuk, mint például
az 5.27 egy szám konstans, és a "foo" egy szöveg konstans.
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
\b
\f
\n
\r
\t
\v
\nnn
\xhh...
awk-ban nem
megengedett.)
\/
awk.
\"
awk a szöveg további részét is figyelembe
vegye.
A gawk-ban van még további két escape szekvencia, aminek speciális
jelentése van a reguláris kifejezésekben,
See section Csak a gawk által értelmezett reguláris kifejezés operátorok.
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:
awk és a
gawk is. Például, "a\qc" ugyanaz mint az "aqc".
awk implementáció
ezt a módszert használja. Ebben az esetben a "a\qc" megfelel a
"a\\qc" szövegnek.
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)
(see section Reguláris kifejezés operátorok)
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
(see section Command Line Options) viszont
mint egyszerű karakter kezeli az így leírt escape szekvenciákat,
például /a\52b/ megegyezik a /a\*b/ mintával.
Összefoglalva:
awk beolvasta.
gawk feldolgozza a konstans és dinamikus reguláris kifejezéseket
(see section Dinamikus reguláris kifejezések használata)
a speciális operátorok számára
(section Csak a gawk által értelmezett 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 táblázatát. Minden olyan karakter, amely nem escape szekvencia és nem szerepel az alábbi táblázatban egyszerűen önmagát jelenti.
\
\$a `$' karakterre illeszkedik.
^
^@chapterilleszkedik a `@chapter'-re egy sor elején, ami alkalmas a fejezetek kezdetének megtalálására egy Texinfo file-ban. A `^' karaktert "horgonynak" is szokták nevezni, mivel a használatával a minta csak a sor elejére illeszkedik, oda horgonyozza a mintát. Fontos tudni, hogy a `^' karakter nem illeszkedik egy szövegben elrejtett új sor kezdetére, így az alábbi feltétel hamis:
if ("line1\nLINE 2" ~ /^L/) ...
$
p$illeszkedik azokra a sorokra, amelyek `p'-vel végződnek. A `$' karakter szintén egy "horgony", és ahogy a `^' karakter úgy a `$' karakter sem illeszkedik az elrejtett új sor végére, tehát az alábbi feltétel is hamis:
if ("line1\nLINE 2" ~ /1$/) ...
.
.Pilleszkedik egy olyan karakterre, amit egy `P' követ a szövegben. Összeállíthatunk olyan reguláris kifejezést is, mint `U.A', amely illeszkedik bármely három karakterre, ami `U' karakterrel kezdődik és az `A' karakterrel végződik. Szigorú POSIX módban (see section Command Line Options), a `.' karakter nem illeszkedik a NUL karakterre. Ennek a karakternek minden bitje zérus, egyébként a NUL karakter is olyan, mint bármely más karakter. Léteznek olyan
awk verziók is,
amelyek nem képesek a NUL karaktert keresni.
[...]
[MVX]illeszkedik az `M', `V' vagy `X' karakterek bármelyikére a szövegben. Karakter tartományok esetén a mínusz karaktert kell használni a kezdő- és a végkarakter között, például a
[0-9]illeszkedik bármelyik számjegyre. Több tartomány is megadható. Pl. a
[A-Za-z0-9] listával írhatjuk le az összes "alfanumerikus"
karaktert.
Ahhoz, hogy a `\', `]', `-' vagy `^' karakterek
valamelyikét figyelembe vehessük a szögletes zárójelek között a
`\' karaktert kell eléjük tenni, például:
[d\]]illeszkedik vagy a `d' vagy a `]' karakterre. A `\' ilyetén használata a karakter listákban kompatíbilis más
awk implementációkkal, és a POSIX szabvány is a fenti megoldást írja
elő. A POSIX által definiált "Kiterjesztett Reguláris Kifejezések" (KRK)
csak egy részhalmaza az awk reguláris kifejezéseinek. A POSIX
"Kiterjesztett Reguláris Kifejezések" a hagyományos egrep
segédprogram által elfogadott reguláris kifejezéseken alapulnak.
A karakterosztályok nem olyan régen lettek bevezetve a
POSIX szabványba. A karakterosztály azon karakterlisták speciális
leírása, amelyek valamilyen értelemben egyedi tulajdonsággal
rendelkeznek, de az aktuális karakterek országonként és/vagy
ábécénként eltérhetnek. Például az ábécé betűi mások az USA-ban,
mint Franciaországban.
A karakter osztályok csak a karakter listák szögletes zárójelein belül
érvényesek. A karakter osztály a `[:' karakterekkel kedődik,
amit az osztályt leíró kulcsszó és a záró `:]' karakterek követnek.
A POSIX szabványban definiált karakter osztályok az alábbiak.
[:alnum:]
[:alpha:]
[:blank:]
[:cntrl:]
[:digit:]
[:graph:]
[:lower:]
[:print:]
[:punct:]
[:space:]
[:upper:]
[:xdigit:]
/[A-Za-z0-9]/ mintát kellett használni. Ha az ábécében más betű
is szerepel (pl. ékezetes karakterek) azokat a minta nem veszi észre. A
POSIX karakter osztályok használatával csak a /[[:alnum:]]/
mintát kell leírni, hogy az ábécé összes betűjét felismerje a
keresésnél.
Van két további speciális karaktersorozat, ami a karakterlistákon
belül előfordulhat. Az egyik azokat a nem ASCII karaktereket
reprezentálja, amelyeket csak több mint egy karakterrel írhatunk le
(több karakterrel leírható szimbólumok), a másik
azokat a karaktereket írja le, amelyeknek nem csak egy megjelenési formája
van, de pl. egy rendezés során azonosnak kell őket tekinteni.
(Például a franciában az egyszerű "e" és az "`e" karakterek
megegyeznek egy rendezés során.)
[[.ch.]] reguláris
kifejezés illeszkedni fog az adott szimbólumra, míg a [ch] vagy a
`c' vagy a `h' karakterre.
[[=e=]] illeszkedhet a `e', `é'
vagy ``e' karakterek bármelyikére.
gawk jelenleg a reguláris kifejezések olyan
implementációját használja, amelyik csak a POSIX szabványban rögzített
karakter osztályokat ismeri fel, és az utóbbi két osztályt nem.
[^ ...]
[^0-9]illeszkedik minden olyan karakterre, amelyik nem számjegy.
|
^P|[0-9]illeszkedik vagy a `^P'-re vagy a `[0-9]', ami azt jelenti, hogy a minta illeszkedik bármely sorra, ami `P' karakterrel kezdődik, vagy tartalmaz egy számjegyet. Az operátor a lehető legnagyobb reguláris kifejezést használja a karakter két oldaláról, vagy más szavakkal a `|' operátornak van a legalacsonyabb precedenciája a reguláris kifejezések között.
(...)
*
ph*esetén a `*' szimbólumot a megelőző `h' karakterre (mint reguláris kifejezésre) alkalmazva egy olyan mintát keres, amiben szerepel egy `p' majd akárhány `h' karakter. Ez a minta az önmagában álló `p' karakterre is illeszkedik. A `*' szimbólum a lehető legrövidebb, megelőző reguláris kifejezésre lesz alkalmazva (hosszabb reguláris kifejezés esetén zárójelet kell használni), és annyi ismétlést próbál találni, amennyi lehetséges. Például:
awk '/\(c[ad][ad]*r x\)/ { print }' sample
kinyomtat a `sample' file-ból minden olyan sort, amely tartalmazza
a `(car x)', `(cdr x)', `(cadr x)', stb. kifejezések
valamelyikét. Fontos a `\' karakter használata a zárójelek előtt.
+
wh+yilleszkedik a `why' és a `whhy' szavakra, de a `wy' szóra nem. Ugyanakkor a `wh*y' minta illeszkedik mind a három szóra. A fenti példa tehát így írható le egyszerűbben:
awk '/\(c[ad]+r x\)/ { print }' sample
?
fe?dilleszkedik a `fed' és `fd' szavakra, de semmi másra nem.
{n}
{n,}
{n,m}
wh{3}y
wh{3,5}y
wh{2,}y
awk-ban, de a
POSIX szabvány azért vezette be őket, hogy az awk és az
egrep következetesen legyenek definiálva.
Mindemellett régi programok használhatják a `{' és a `}'
karaktereket reguláris kifejezésekben, ezért a gawk alapesetben
nem engedi meg intervallum kifejezések használatát a reguláris
kifejezésekben. Ha `--posix' vagy a `--re-interval' parancssori
opció (see section Command Line Options) definiálva van,
intervallum kifejezések megengedettek a reguláris kifejezésekben.
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
(see section Command Line Options) 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.
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.
\w
[[:alnum:]_].
\W
[^[:alnum:]_].
\<
/\<away/ minta illeszkedik a `away'
szóra, de nem a `stowaway' szóra.
\>
/stow\>/ minta illeszkedik a `stow'
szóra, de nem a `stowaway' szóra.
\y
\B
/\Brat\B/ minta illeszkedik a `crate' szóra, de nem
illeszkedik a `dirty rat' kifejezésre. A `\B' operátor
lényegében a `\y' operátor ellentéte.
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.
Megoldás lenne ha a GNU operátoroknál két `\' jelet kellene
használni, de ez valószínűleg túl zavaró lenne, ezért használja a
gawk a `\y' operátort, ami talán két rossz
megoldás közül a kevésbé rossz.
A különböző parancssori opciók (see section Command Line Options)
befolyásolják a gawk interpretert, hogy hogyan értelmezze a
reguláris kifejezésekben előforduló karaktereket:
gawk biztosítja az POSIX reguláris
kifejezés operátorok és a GNU operátorok,
section Reguláris kifejezés operátorok,
használatát, de intervallum kifejezéseket nem lehet használni.
--posix
--traditional
awk reguláris kifejezéseket lehet csak
használni. A GNU operátorok, intervallum kifejezések és karakterosztályok
([[:alnum:]] és így tovább) nem állnak rendelkezésre.
Oktálisan vagy hexadecimálisan leírt karakterek önmagukat jelentik,
még akkor is ha reguláris kifejezés metakarakterek lennének normális
esetben.
--re-interval
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,
see section Szövegmanipuláló beépített függvények).
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.
(5)
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
(see section Other Command Line Arguments; és
see section Kezdő és lezáró tevékenységek)
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 (see section Command Line Options).
Ebben az esetben a kis- és nagybetűk közötti különbség mindig fontos.
echo aaaabcd | awk '{ sub(/a+/, "<A>"); print }'
Ez a példa a sub függvényt használja, (amit eddig még nem
tárgyaltunk,
see section Szövegmanipuláló beépített függvények)
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
(see section Hogyan történik a feldarabolás rekordokra és
see section Hogyan történik a mezőelválasztás)
kell csinálni és a
match, sub, gsub és a gensub függvényeket kell
használni, akkor ez nagyon fontossá válhat.
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.
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.
A tipikus awk programokban a bemenet vagy a standard
bemenet (általában a billentyűzet, de gyakran egy cső (pipe) valamilyen
másik parancsból), vagy file-ok, amelyek nevét az awk parancssorában
kell megadni. Az awk a bemeneti file-okat sorrendben olvassa be,
és előbb befejezi az egyiket, mielőtt elkezdené a következőt. Az
éppen aktuális file nevét a FILENAME
(see section Beépített változók)
beépített változóból lehet kiolvasni.
Az awk a bemenetet rekordokban olvassa be és dolgozza fel a
programodban megadott szabályok szerint, egyszerre csak egyet.
Alapesetben egy rekord egy sornak felel meg. Ezenkívül automatikusan
minden rekordot feldarabol úgynevezett mezőkre, ezzel kényelmesebbé
téve a rekord részeinek elérését a programod számára.
Ritkán, de előfordulhat, hogy a getline parancsot kell használnod.
A getline parancs nagyon fontos része a nyelvnek, mivel adatot tud
beolvasni bármilyen és bármennyi file-ból, ráadásul ezeket a file-okat
nem kötelező megadni az awk parancssorában
(see section Explicit beolvasás getline-al).
Az awk segédprogram a bemenetet rekordokra és mezőkre darabolja fel
a programod számára. A rekordok egy úgynevezett rekordelválasztóval
vannak elválasztva egymástól. Alapesetben a rekordelválasztó az új sor
karakter. Ezért van az, hogy alapesetben egy sor megfelel egy rekordnak. Más
karakter is megadható mint rekordelválasztó, ha az RS beépített
változót beállítjuk a kívánt karakterre.
Az RS értékének megváltoztatása ugyanúgy történik mint bármilyen
más változóé, az értékadó operátorral, `='
(see section Értékadó kifejezések). Az új rekordelválasztót
idézőjelek közé kell tenni, mint egy szöveg konstanst. Általában az
értékadást a legjobb azelőtt végrehajtani mielőtt bármilyen adatot a program
feldolgozna, így minden adat a megfelelő elválasztó karakterrel lesz kezelve.
Ehhez a BEGIN
(see section A BEGIN és az END speciális minták)
szabályt kell használni. Például:
awk 'BEGIN { RS = "/" } ; { print $0 }' BBS-list
megváltoztatja az RS értékét a "/" karakterre mielőtt bármilyen
adatot beolvasna, így a rekordelválasztó karakter a "/" lesz. Ezután
elkezdi olvasni a file tartalmát és az awk program második szabályát
alkalmazza (tevékenységet minta nélkül), ami kinyomtat minden rekordot. Mivel
a print kifejezés minden kinyomtatott rekordhoz egy új sort ad, a
program lényegében a bemenetet a kimenetre másolja át úgy, mintha minden
"/" karaktert lecserélnénk egy új sor karakterre. Az eredmény a
`BBS-list' file-al:
$ awk 'BEGIN { RS = "/" } ; { print $0 }' BBS-list
-| aardvark 555-5553 1200
-| 300 B
-| alpo-net 555-3412 2400
-| 1200
-| 300 A
-| barfly 555-7685 1200
-| 300 A
-| bites 555-1675 2400
-| 1200
-| 300 A
-| camelot 555-0542 300 C
-| core 555-2912 1200
-| 300 C
-| fooey 555-1234 2400
-| 1200
-| 300 B
-| foot 555-6699 1200
-| 300 B
-| macfoo 555-6480 1200
-| 300 A
-| sdace 555-3430 2400
-| 1200
-| 300 A
-| sabafoo 555-2127 1200
-| 300 C
-|
Érdemes megfigyelni, hogy a `camelot' kezdetű sor nem lett feldarabolva. Az eredeti file-ban (see section Adat file-ok a példákhoz) a sor így néz ki:
camelot 555-0542 300 C
Csak egyetlen baud érték van megadva és nincs "/" karakter
a sorban.
Egy másik lehetőség a rekordelválasztó megváltoztatására a parancssor használata (see section Other Command Line Arguments).
awk '{ print $0 }' RS="/" BBS-list
Ez a parancssor beállítja az RS változót a `/' karakterre,
mielőtt elkezdené a `BBS-list' file feldolgozását.
A speciális karakter, mint a `/' karakter használata az esetek
legnagyobb részében nem okoz problémát, de a következő speciális
parancssor csak egy meglepő `1'-est nyomtat ki. Az NF
beépített változó értéke az aktuális rekordon belüli mezők számát adja meg.
Jelen esetben egyetlen mező van, ami az új sor karaktert tartalmazza.
$ echo | awk 'BEGIN { RS = "a" } ; { print NF }'
-| 1
Amint eléri a bemenet végét a jelenlegi rekordot lezárja, még akkor is ha az utolsó karakter a file-ban nem a rekordelválasztó volt (s.s.).
Az üres szövegnek, "" (szöveg, amiben nincs karakter), mint
az RS értéke, speciális
jelentése van: azt jelenti, hogy a rekordok egy vagy több üres sorral
vannak elválasztva és semmi mással.
See section Több sorból álló rekordok, további részletekért.
Ha az RS értékét az awk futása közben változtatod meg, akkor
az új értéket csak az új rekord beolvasásától kezdve veszi figyelembe,
az éppen aktuális (és az előzőleg feldolgozott) rekordokat nem befolyásolja.
Miután megtalálta a rekord végét, a gawk az
RT változót beállítja arra a karakterre/szövegre, ami illeszkedett
az RS rekordelválasztóra.
Valójában az RS értéke nem csak egy karakter lehet, hanem
bármilyen reguláris kifejezés (see section Reguláris kifejezések).
Általában egy rekord a megadott reguláris
kifejezés kezdeténél végződik; a következő rekord az illeszkedő reguláris
kifejezés végénél kezdődik. Ez a szabály érvényesül alapesetben is, amikor
az RS az új sor karakter: a rekord a következő illeszkedő reguláris
kifejezésnél ér véget (az új sor karakternél), a következő rekord pedig
a reguláris kifejezés végénél kezdődik (vagyis a következő sor első
karakterénél). Mivel az új sor karakter illeszkedik az RS-re
ezért egyik rekordnak sem része.
Amikor az RS értéke csak egy karakter, akkor az RT is ugyanazt a
karaktert fogja tartalmazni. Ugyanakkor, ha az RS értéke egy reguláris
kifejezés az RT sokkal hasznosabb lehet; azt az aktuális
szövegrészletet tartalmazza, ami illeszkedett a reguláris kifejezésre.
A fentieket az alábbi példa illusztrálja, ahol az RS egy olyan
reguláris kifejezés amelyik vagy az új sor karakterre vagy egy olyan
szövegre illeszkedik, amely egy vagy több nagybetűt tartalmaz, ill.
előtte és/vagy mögötte egy szóköz karakter lehet
(see section Reguláris kifejezések).
$ echo record 1 AAAA record 2 BBBB record 3 |
> gawk 'BEGIN { RS = "\n|( *[[:upper:]]+ *)" }
> { print "Record =", $0, "and RT =", RT }'
-| Record = record 1 and RT = AAAA
-| Record = record 2 and RT = BBBB
-| Record = record 3 and RT =
-|
Az utolsó sor egy üres sor. Ez azért van, mert az utolsó RT
értéke egy új sor és a print kifejezés mindig hozzáad egy
lezáró új sor karaktert a kimenethez.
See section A Simple Stream Editor, ahol további példák
találhatók az RS és RT használatára.
Az RS használata mint reguláris kifejezés és az RT változó
az awk nyelv gawk kiegészítései; "compatibility" módban
nem használhatók (see section Command Line Options).
"Compatibility" módban az RS-nek csak az első karakterét használja a
rekord végének megállapítására.
Az awk segédprogram azt is számontartja, hogy eddig hány darab
rekordot olvasott be az aktuális bemeneti file-ból. Ezt az értéket az
FNR beépített változóban lehet megtalálni. Amikor egy új file-t
kezd el olvasni a változó értéke mindig lenullázódik. Egy másik beépített
változó, az NR, az összes file-ból az összes eddig beolvasott
rekordok számát tárolja. Kezdeti értéke zérus és soha nem nullázódik le
automatikusan.
A beolvasott rekordot az awk automatikusan feldarabolja mezőkre.
Alapesetben a mezőket szóközök vagy tab vagy új sor karakterek(6) választják
el, mint a szavakat egy mondatban; hasonló karakterek, mint lapdobás (formfeed)
nem szolgálnak elválasztóként az awk-ban.
A mezők célja, hogy kényelmesebbé tegyék számodra a rekordok feldolgozását.
Nem kötelező őket használni -- dolgozhatsz csak a rekorddal -- de a mezők
teszik az awk programokat igazán hasznossá.
Az awk programban egy mezőre egy dollár jellel, `$', és az utána
következő számmal lehet hivatkozni. Így, a $1 az első, a $2 a
második mezőre utal. Vegyük a következő sort:
This seems like a pretty nice example.
Itt az első mező vagy $1 a `This'; a második mező vagy
$2 a `seems' és így tovább. Az utolsó mező vagy $7
az `example.'. Mivel nincs szóköz a utolsó `e' betű és a lezáró
`.' pont között ezért a pont a mező része lesz.
Az NF beépített változó adja meg, hogy az aktuális rekordban hány mező
van. Az awk automatikusan frissíti az NF értékét minden új rekord
beolvasásánál.
Akárhány mező van a rekordban, az utolsó rekordra a $NF-el is
lehet hivatkozni. Így a fenti példában az $NF ugyanaz lenne
mint a $7, ami az `example.'. Hogy ez miért működik, azt egy
kicsit később magyarázzuk el. Ha az utolsó utáni mezőre hivatkozol, például
a $8-ra amikor csak hét mező van a rekordban, egy üres szöveget kapsz
eredményül.
A $0 egy speciális eset, a teljes rekordot reprezentálja. $0-t
használhatod ha a mezőkkel nem akarsz foglalkozni.
Még egy példa:
$ awk '$1 ~ /foo/ { print $0 }' BBS-list
-| fooey 555-1234 2400/1200/300 B
-| foot 555-6699 1200/300 B
-| macfoo 555-6480 1200/300 A
-| sabafoo 555-2127 1200/300 C
Ez a példa minden olyan rekordot kinyomtat a `BBS-list' file-ból,
amelynek az első mezőjében előfordul a `foo' szó. A `~'
operátor az illesztő operátor
(see section Hogyan használjuk a reguláris kifejezéseket);
azt ellenőrzi, hogy a megadott kifejezés (itt a $1 mező)
illeszkedik-e a reguláris kifejezésre.
Ezzel ellentétben, a következő példa a `foo' szót keresi a teljes rekordban és csak az első és az utolsó mezőt nyomtatja ki az illeszkedő rekordoknál.
$ awk '/foo/ { print $1, $NF }' BBS-list
-| fooey B
-| foot B
-| macfoo A
-| sabafoo C
A mezőazonosító szám nem csak konstans lehet. Az awk nyelvben
a `$' karakter után bármilyen kifejezés állhat. A kifejezés értéke
adja meg a mező számát. Ha kifejezés értéke szöveg, akkor azt átalakítja
számmá. Például:
awk '{ print $NR }'
Ha emlékszem, akkor az NR az eddig beolvasott rekordok számát
tartalmazza; az első rekordnál egy, a másodiknál kettő, és így tovább,
az értéke. Így ez a példa kinyomtatja az első mezőt az első rekordnál,
a második mezőt a második rekordnál, és így tovább. A huszadik rekordnál
a huszadik mezőt nyomtatja ki; de mivel valószínűleg a rekordban nincs
20 mező ezért csak egy üres sort fog kinyomtatni.
Itt egy másik példa, ahol egy kifejezést használunk a mezőazonosító számnak:
awk '{ print $(2*2) }' BBS-list
Az awk először kiértékeli a `(2*2)' kifejezést, majd az
eredményül kapott számot használja a mező azonosítására. A `*'
jel szorzást jelent, így a `2*2' kifejezés értéke négy lesz. A
zárójelek azért kellenek, hogy előbb a szorzás hajtódjon végre és csak
utána a mező azonosítás; zárójelek mindig kellenek, ha matematikai
műveletet használunk a mezőazonosító szám előállítására. Végül is ez
a példa a `BBS-list' file minden sorából a negyedik mezőt fogja
kinyomtatni. (Az awk nyelv operátorainak a precedencia listája,
csökkenő sorrendben a
section Operátorok precedenciája (Hogyan ágyazhatók operátorok egymásba)
alatt található meg.)
Ha a mezőazonosító szám a kiértékelés után zérus lesz, akkor a teljes
rekordot kapod eredményül. Így a $(2-2) kifejezés ugyanaz mint
a $0. Negatív számok nem megengedettek mezőazonosítóként; ha
mégis előfordulna, akkor valószínűleg az awk program leáll.
(A POSIX szabvány nem definiálja a viselkedést negatív szám esetére.
A gawk leállítja a programot negatív szám esetén, más awk
implementációk másképp viselkedhetnek.)
Ahogy azt korábban elmondtuk, section Mezők elérése, az
NF beépített változó
(see section Beépített változók)
a mezők számát
adja meg az aktuális rekordban. Így a $NF kifejezés nem egy
speciális kifejezés, csak a közvetlen következménye az NF
használatának, mint mezőazonosító szám.
Egy mező tartalmát meg is változtathatod a programon belül.
(Bár ez a bemenetet megváltoztatja az awk számára, valójában a tényleges
bemenet változatlan; az awk soha nem módosítja a bemeneti file-t.)
Vegyük a következő példát és a kimenetét:
$ awk '{ $3 = $2 - 10; print $2, $3 }' inventory-shipped
-| 13 3
-| 15 5
-| 15 5
...
A `-' jel a kivonás, így ez a program a harmadik mezőnek, $3,
új értéket ad, a második mezőből tizet vonva ki, `$2 - 10'.
(See section Matematikai operátorok.) Ezután a második és a
harmadik mezőt kinyomtatja az új értékkel.
Ahhoz, hogy ez működjön, a második mezőnek, $2, olyan szöveget kell
tartalmaznia, ami egy értelmes szám; a szöveget átalakítja számmá, hogy a
matematikai műveletet elvégezhesse. A kivonás eredményét átalakítja
szöveggé, amit végül hozzárendel a harmadik mezőhöz.
See section Szövegek és számok konverziója.
Amikor egy mező tartalmát megváltoztatod, az awk a rekord szövegét
újra kiértékeli, hogy a rekord tükrözze a változást. Így a $0
a megváltozott mezőt fogja tartalmazni. A következő program a bemeneti
file-t nyomtatja ki úgy, hogy minden sorban a második mező értékéből
tizet kivon.
$ awk '{ $2 = $2 - 10; print $0 }' inventory-shipped
-| Jan 3 25 15 115
-| Feb 5 32 24 226
-| Mar 5 24 34 228
...
Olyan mezőkhöz is rendelhető tartalom, amelyek nem részei a rekordnak. Például:
$ awk '{ $6 = ($5 + $4 + $3 + $2)
> print $6 }' inventory-shipped
-| 168
-| 297
-| 301
...
A $6 nem létezik a rekordban, mi hoztuk létre és a
$2, $3, $4 és a $5 mezők tartalmának összegével
töltöttük fel. A `+' jel összeadást jelent. Az `inventory-shipped'
file esetén a $6 mező az egy hónapban elküldött összes csomag
számát jelenti.
Egy új mező létrehozása esetén, a bemeneti rekord belső reprezentációja
is megváltozik -- a $0 értéke. Így a mező létrehozása után egy
`print $0' kifejezés kinyomtatja a rekordot az új mezővel együtt,
a megfelelő mezőelválasztót használva.
A rekord új kiértékelése befolyásolni fogja az NF értékét
(a mezők száma; see section Mezők elérése), ugyanakkor az
NF és az eddig nem tárgyalt kimeneti mezőelválasztó,
OFS (see section Kimeneti elválasztó) is befolyásolva
lesz a kiértékelés által. Például az általad létrehozott
legnagyobb mezőazonosító számot fogja az NF tartalmazni.
Ugyanakkor csak egy egyszerű hivatkozás egy olyan mezőre, ami nem szerepel
a rekordban, nem fogja megváltoztatni sem a $0 sem az NF
értékét. A hivatkozás egy üres szöveget fog generálni, például:
if ($(NF+1) != "")
print "can't happen"
else
print "everything is normal"
program részlet az `everything is normal' szöveget fogja kinyomtatni,
mivel a NF+1-ik mező természetesen nem szerepel a rekordban.
(Az awk if-else kifejezésről további információ a
see section Az if-else kifejezés alatt található.
A `!=' operátorról pedig a
section Változó típusok és az összehasonlító kifejezések
ad további információt.)
Fontos megjegyezni, hogy egy létező mező tartalmának megváltoztatása
befolyásolni fogja a $0 értékét, de nem fogja megváltoztatni
NF értékét, még akkor sem ha a mező új értéké az üres szöveg.
Például:
$ echo a b c d | awk '{ OFS = ":"; $2 = ""
> print $0; print NF }'
-| a::c:d
-| 4
A mező még mindig ott van; csak éppen üres, amit a két egymást követő kettőspont is jelez.
Ez a példa bemutatja, hogy mi történik, ha egy új mezőt hozunk létre.
$ echo a b c d | awk '{ OFS = ":"; $2 = ""; $6 = "new"
> print $0; print NF }'
-| a::c:d::new
-| 6
A közbenső, $5 mező is létrejön egy üres szöveggel
(a második dupla kettőspont mutatja) és az NF értékét hatra
állítja.
Végül, ha az NF értékét csökkentjük, akkor mező(ke)t dobunk el
és a $0 új értéket kap a kiértékelés után. Például:
$ echo a b c d e f | ../gawk '{ print "NF =", NF;
> NF = 3; print $0 }'
-| NF = 6
-| a b c
Ez a fejezet egy kicsit hosszabb lesz, mivel az awk egyik alapvető
működési elvét magyarázza el.
A mezőelválasztó, vagy egy karakter vagy egy reguláris kifejezés,
adja meg, hogy az awk hogyan darabolja fel a bemeneti rekordot
mezőkre. Az awk a mezőelválasztóra illeszkedő karaktersorozatokat
keres a bemeneti rekordban és a mezők azok a szövegek lesznek amelyek az
illeszkedő részek között helyezkednek el.
Az alábbi példákban a szóköz helyett a "*" jelet fogjuk használni a kimenetben.
Ha a mezőelválasztó az `oo', akkor az alábbi sor:
moo goo gai pan
az `m', `*g' és a `*gai*pan' mezőkre lesz feldarabolva. A második és a harmadik mező előtti szóköz is a mező része lesz.
A mezőelválasztót az FS beépített változó tartalmazza. Shell
programozók figyelem! Az awk nem használja az
IFS nevet, amit a POSIX szabványos shell-ek használnak
(Bourne shell, sh, vagy a GNU Bourne-Again Shell, Bash).
Egy awk programon belül az FS értékét az `=' értékadó
operátorral változtathatod meg (see section Értékadó kifejezések).
A legjobb alkalom erre a program eleje, mielőtt bármilyen adatot a program
beolvasna, így a legelső rekord is a megfelelő mezőelválasztóval lesz
feldolgozva. Például a BEGIN minta
(see section A BEGIN és az END speciális minták)
használatával teheted ezt meg. Az alábbi példában az FS értéke a
"," lesz:
awk 'BEGIN { FS = "," } ; { print $2 }'
és ha a bemeneti sor:
John Q. Smith, 29 Oak St., Walamazoo, MI 42139
akkor az awk program a `*29*Oak*St.'
szöveget fogja kinyomtatni.
Előfordul, hogy az adatod olyan helyen is tartalmaz elválasztó karaktert, ahol azt nem várnád. Például személy nevek esetén a fenti példa sorban tudományos cím vagy egyéb adat is megadható, mint `John Q. Smith, LXIX'. Tehát:
John Q. Smith, LXIX, 29 Oak St., Walamazoo, MI 42139
sor esetén a program a `*LXIX'-et fogja kinyomtatni és nem a `*29*Oak*St.'. Ha a lakcímeket szeretted volna kigyűjteni, természetesen meglepődnél. A tanulság az, hogy az adatstruktúrát és az elválasztó karaktereket gondosan kell megválasztani, hogy az ilyen problémák elkerülhetők legyenek.
Amint azt tudod, alapesetben a mezőket szóközök, tab és új sor karakterek
választják el egymástól; nem csak egy szóköz: két szóköz egymás után nem
generál egy üres mezőt. Az FS alapértéke a " ", egy szóközt
tartalmazó szöveg. Ha ezt a normális módon értelmeznénk, minden szóköz egy
mezőt választana el, így két szóköz egymás után egy üres mezőt generálna.
Az ok amiért nem ez történik az, hogy egyetlen szóköz mint az FS
értéke egy speciális eset.
Ha az FS bármilyen más karaktert tartalmaz, pl. a ",",
akkor a karakter minden előfordulása két mezőt választ el egymástól.
Két egymás utáni megjelenése egy üres mezőt határol. Ha egy rekord
elején vagy végén fordul elő, az is üres mezőt jelent. A szóköz
karakter az egyetlen kivétel, ami nem követi ezt a szabályt.
Az előző alfejezet bemutatta egy karakter használatát mint mezőelválasztó.
Általánosítva, az FS értéke bármilyen reguláris kifejezés lehet. Ebben
az esetben, minden illeszkedés a rekordon belül két mezőt választ el
egymástól. Például:
FS = ", \t"
esetén minden olyan szöveg, ami egy vesszőből, egy szóköz és egy tab karakterből áll, elválaszt két mezőt. (`\t' egy escape szekvencia (see section Escape szekvenciák), ami a tab karaktert helyettesíti.)
Egy kicsit bonyolultabb példa: tegyük fel, hogy a szóköz karaktert ugyanúgy
szeretnéd használni, mint más karaktereket a mezők elválasztására. Ebben az
esetben az FS-t a "[ ]" reguláris kifejezésre kell
beállítani (nyitó szögletes zárójel, szóköz, záró szögletes zárójel).
Ez a reguláris kifejezés csak egy szóközre illeszkedik és semmi másra
(see section Reguláris kifejezések).
Van egy fontos különbség a `FS = " "' és a
`FS = "[ \t\n]+"' kifejezések között. Mindkét esetben a mezőket
egy vagy több szóköz, tab és/vagy új sor karakter választja el, de amikor az
FS értéke a " ", az awk először levágja a kezdő és
záró szóközöket, tab és új sor karaktereket és csak utána kezdi el
feldolgozni a rekordot.
Az alábbi példa csak egy `b'-t fog kinyomtatni:
$ echo ' a b c d ' | awk '{ print $2 }'
-| b
de ez egy `a'-t nyomtat ki (extra szóközök vannak a betűk körül):
$ echo ' a b c d ' | awk 'BEGIN { FS = "[ \t]+" }
> { print $2 }'
-| a
Ebben az esetben az első mező üres.
A kezdő és záró szóköz, tab és új sor karakterek levágása a $0 új
kiértékelésénél is fontos szerepet játszik, így:
$ echo ' a b c d' | awk '{ print; $2 = $2; print }'
-| a b c d
-| a b c d
Az első print kifejezés kinyomtatja az eredeti rekordot. A
$2 mező értékadása után újraértékeli a $0 tartalmát;
a $1-tól az $NF-ig a mezőket összefűzi az OFS
tartalmát használva elválasztásra. Mivel a kezdő és záró szóköz
karaktereket nem vette figyelembe a $1 megállapításánál,
így azok most sem részei az új $0-nak. Végül az utolsó
print kifejezés kiírja az $0 új tartalmát.
Előfordulhat, hogy egy rekord minden karakterét meg szeretnéd vizsgálni
külön-külön. gawk-ban ez egyszerű, egy üres szöveget ("")
kell az FS-hez rendelni. Ebben az esetben minden karakter egy önálló
mező lesz:
$ echo a b | gawk 'BEGIN { FS = "" }
> {
> for (i = 1; i <= NF; i = i + 1)
> print "Field", i, "is", $i
> }'
-| Field 1 is a
-| Field 2 is
-| Field 3 is b
Hagyományosan, ha az FS értéke "", akkor az awk
viselkedése nem definiált. Ebben az esetben a Unix awk az
egész rekordot egy mezőnek tekinti (s.s.). "Compatibility"
módban (see section Command Line Options), ha az FS értéke
"" a gawk is így viselkedik.
FS beállítása parancssorból
Az FS értéke a parancssorból is beállítható, a `-F' opció
használatával:
awk -F, 'program' input-files
hatására az FS értéke a `,' karakter lesz. Fontos észrevenni,
hogy az opciót a nagy `F' betűvel lehet megadni, míg a kis `f'
betű az awk programot tartalmazó file-t adja meg. A
`-F' és `-f' -nek semmi köze egymáshoz, a kis- és nagybetű
megkülönböztetés fontos. Természetesen a két opciót használhatod
egyszerre.
A `-F' utáni argumentum feldolgozása ugyanúgy történik mintha
az FS-t a programban állítanánk be. Ez azt jelenti, hogy ha a
mezőelválasztó egy speciális karaktert tartalmaz, akkor azt megfelelően
védeni kell a `\' karakterrel. Például ha a mezőelválasztó egy
`\' karakter, akkor ezt kell begépelni:
# ugyanaz mint FS = "\\" awk -F\\\\ '...' files ...
Mivel a `\' karakter a shell számára is speciális karakter, ezért az
awk csak a `-F\\' kifejezést fogja megkapni. Ezután az
awk feldolgozza a `\\' escape szekvenciát
(see section Escape szekvenciák), ami végül a
`\' jelet adja meg mint mezőelválasztó.
Egy speciális eset, ha "compatibility" módban
(see section Command Line Options) az `-F' után csak egy
`t' betű áll. Ekkor az FS valójában a tab karaktert
kapja értékül. Ez azért van, mert ha a `-F\t' gépeled be
idézőjelek nélkül, akkor a `\' jelet a shell eldobja
és az awk azt gondolja, hogy a tab karaktert akartad megadni és nem
a `t' betűt, mint mezőelválasztó. Ha tényleg csak a `t' betűvel
akarod elválasztani a mezőket, akkor a `-v FS="t"' kifejezést kell
használni (see section Command Line Options).
Például készítsünk egy `baud.awk' nevű file-t, ami a
/300/ mintát és a `print $1' tevékenységet tartalmazza:
/300/ { print $1 }
Állítsuk be az FS-t a `-' karakterre, majd futtassuk a programot
a `BBS-list' file-al. Az alábbi parancs kilistázza azoknak a
gépeknek a nevét és a telefonszámuk első három jegyét, amelyek
300 baud-al működnek:
$ awk -F- -f baud.awk BBS-list -| aardvark 555 -| alpo -| barfly 555 ...
A második sor nem egészen tökéletes. Az eredeti file-ban (see section Adat file-ok a példákhoz) így nézett ki:
alpo-net 555-3412 2400/1200/300 A
A `-' karakter szerepel a rendszer nevében, így nem a telefonszámot írja ki, ahogy azt szeretnénk. Ez is mutatja mennyire fontos, hogy gondosan válasszuk meg a mezőket és a mezőelválasztókat.
A Unix rendszereken a jelszó (passwd) file-ban minden felhasználóhoz tartozik egy bejegyzés (egy sor). A mezők kettősponttal vannak elválasztva. Az első mező a bejelentkezési név, a második a felhasználó jelszava. (A legtöbb rendszeren ma már nem elérhető a jelszó a felhasználók számára.) Egy bejegyzés így nézhet ki:
arnold:xyzzy:2076:10:Arnold Robbins:/home/arnold:/bin/sh
Az alábbi program végignézi a jelszó file-t és kilistázza azokat a felhasználókat akiknél nincs jelszó megadva:
awk -F: '$2 == ""' /etc/passwd
A POSIX szabvány szerint az awk-nak úgy kell viselkednie,
mintha minden rekordot a beolvasás során darabolna fel mezőkre. Ez azt
jelenti, hogy ha megváltoztatod az FS értékét miután a rekordot
beolvasta, akkor a mezők feldarabolása azt az állapotot kell tükrözze, ami a
régi FS használatával érvényes.
Ugyanakkor sok awk implementáció nem így működik. Ezek a programok
csak akkor darabolják fel a a rekordot mezőkre, amikor hivatkoznak egy
mezőre, így a mezők az éppen aktuális FS mezőelválasztó szerint
lesznek megállapítva. (s.s.) Ezt a viselkedést nehéz felfedezni. Az
alábbi program illusztrálja a különbséget a két megoldás között.
(A sed(7) parancs a `/etc/passwd' file-nak csak az első sorát
nyomtatja ki.)
sed 1q /etc/passwd | awk '{ FS = ":" ; print $1 }'
Egy rossz awk implementáció esetén a program a
root
sort nyomtatja ki, míg a gawk valami ehhez hasonlót fog kiírni:
root:nSijPlPhZZwgE:0:0:Root:/:
Az alábbi táblázat összefoglalja, hogy az FS értékétől függően
a mezők hogyan lesznek elválasztva . (A `==' jelentése
egyenlő.)
FS == " "
FS == egy bármilyen karakter
FS == regexp
FS == ""
(Ezt a fejezetet kezdő felhasználók nyugodtan átugorhatják első olvasásnál, mivel az itt leírt programozási lehetőség csak kísérleti, és bonyolult lehet megérteni.)
A gawk 2.13-as verziója vezette be azt a megoldást, amivel
adott szélességű mezőket lehet kezelni, és nincs mezőelválasztó.
Régi FORTRAN programok bemeneteként fordulhat elő ilyen adat, ahol
a számok között nincs elválasztás.
Lényegében egy olyan táblázatról beszélünk, ahol az oszlopok
szóközökkel vannak igazítva és az üres mező csak egy szóköz.
Ilyen környezetben az awk mezőelválasztó stratégiája nem igazán
tökéletes. Habár egy hordozható program
a substr függvény használatával meg tudja oldani a problémát
(see section Szövegmanipuláló beépített függvények),
a megoldás nem túl szép és különösen nem lenne hatékony sok rekord esetén.
A rekord adott szélességű mezőkre darabolásához a FIELDWIDTHS
beépített változónak kell egy olyan szöveget megadni, ahol a szövegben
a mezők szélességét jelentő számokat szóközök választanak el. Minden
szám egy mező szélességét adja meg, a mezők közti szóközöket is
beleszámolva. Ha bizonyos oszlopokkal nem akarsz foglalkozni, akkor
definiáld egy külön mezőbe a szélesség megadásával, majd ne vedd
figyelembe a keletkezett mezőt.
Az alábbi adatsor a w Unix segédprogram kimenete és alkalmas
a FIELDWIDTHS használatának bemutatására.
10:06pm up 21 days, 14:04, 23 users User tty login idle JCPU PCPU what hzuo ttyV0 8:58pm 9 5 vi p24.tex hzang ttyV3 6:37pm 50 -csh eklye ttyV5 9:53pm 7 1 em thes.tex dportein ttyV6 8:17pm 1:47 -csh gierd ttyD3 10:00pm 1 elm dave ttyD4 9:47pm 4 4 w brent ttyp0 26Jun91 4:46 26:46 4:41 bash dave ttyq4 26Jun9115days 46 46 wnewmail
Az alábbi program a fenti adatból kiválogatja az üresjárati (idle) időtartam hosszát, átkonvertálja másodpercbe, majd kiírja az első két mezőt és az üresjárati időt másodpercben. (A program olyan megoldásokat is tartalmaz, amiket eddig nem tárgyaltunk.)
BEGIN { FIELDWIDTHS = "9 6 10 6 7 7 35" }
NR > 2 {
idle = $4
sub(/^ */, "", idle) # strip leading spaces
if (idle == "")
idle = 0
if (idle ~ /:/) {
split(idle, t, ":")
idle = t[1] * 60 + t[2]
}
if (idle ~ /days/)
idle *= 24 * 60 * 60
print $1, $2, idle
}
Az eredmény:
hzuo ttyV0 0 hzang ttyV3 50 eklye ttyV5 0 dportein ttyV6 107 gierd ttyD3 1 dave ttyD4 0 brent ttyp0 286 dave ttyq4 1296000
Egy másik (talán praktikusabb) példa a szavazókártyák feldolgozása.
Az USA egyes területein úgy kell szavazni, hogy lyukat kell ütni egy
kártyába. Ezeket a kártyákat használjak a szavazatszámlálás során.
Mivel az is előfordulhat, hogy valaki az adott kérdésben
nem akar szavazni egyes oszlopok üresek lehetnek. Az ilyen adatok
feldolgozására az gawk jól használhatná a FIELDWIDTHS
megoldást. (Persze az egy másik kérdés, hogy a gawk
hogyan kerülne a kártyaolvasó gépbe!)
Ha értéket adunk az FS változónak a gawk visszatér az
eredeti mező darabolási metódushoz. Mivel valószínűleg nem akarod tudni
az FS értékét, csak visszakapcsolni normál módba,
használhatod a `FS = FS' kifejezést is.
Ez a lehetőség még csak kísérleti, idővel változhat. Figyelj oda, mert
a gawk egyáltalán nem ellenőrzi a FIELDWIDTHS-nek megadott
értékeket.
Ha egy adatbázisban egy sor nem tudja kényelmesen tárolni az összes információt, akkor több soros rekordot érdemes használni.
Az első lépés a megfelelő adatformátum kiválasztása: hogyan definiálsz egy rekordot? Mi választja el a rekordokat?
Az egyik megoldás valamilyen szokatlan karakter vagy szöveg használata
rekordelválasztóként. Például használhatod a lapdobás (formfeed)
karaktert (`\f' az awk-ban mint a C programozási nyelvben is),
így minden rekord egy oldal a file-ban. Ehhez csak az RS
változót kell az "\f"-re beállítani (egy olyan szövegre, ami csak
a a lapdobás karaktert tartalmazza). Bármilyen más karakter is megfelelő,
ha biztos vagy benne, hogy nem fog a rekordon belül előfordulni.
A másik megoldás, hogy üres sorok választják el a rekordokat. Ha az
RS értéke egy üres szöveg, akkor a rekordokat egy vagy több üres sor
választhatja el. Ebben az esetben a rekord mindig az első üres sornál
ér véget, és a következő rekord az első nem üres sornál kezdődik -
nem számít, hogy hány üres sor van a két rekord között, mindig egy
elválasztóként lesznek kezelve.
Ugyanezt a hatást érheted el az "\n\n+" kifejezés használatával.
Ez a reguláris kifejezés illeszkedik a rekord utáni új sorra és a
követő egy vagy több üres sorra. Ráadásul a reguláris kifejezések
a lehető leghosszabb mintára illeszkednek
(see section Mennyi szöveg illeszkedik?), így a
következő rekord csak az üres sorok után kezdődik -
nem számít, hogy hány üres sor van a két rekord között, mindig egy
elválasztóként lesznek kezelve.
Van egy fontos különbség a `RS = ""' és a `RS = "\n\n+"' kifejezések között. Az első esetben az adat file-ban előforduló kezdő és záró üres sorokat nem veszi figyelembe, egyszerűen eldobja azokat. A második esetben ez nem történik meg. (s.s.)
Most, hogy a bemenetet feldaraboltuk több sorból álló rekordokra,
a második lépés a rekordokon belüli mezők megállapítása. Az egyik
megoldás, hogy a sorokat hagyományos módon feldaraboljuk mezőkre,
de amikor az RS értéke egy üres szöveg az új sor karakter
mindig mezőelválasztóként viselkedik. Ez csak egy ráadás az
FS-ben megadott elválasztóhoz.
Ugyanakkor ez probléma is lehet, ha az új sor karaktert nem akarod
mezőelválasztóként használni. Mivel ezt a speciális beállítást kikapcsolni
nem lehet, csak a split függvény használatával tudod megfelelően
feldarabolni a rekordot
(see section Szövegmanipuláló beépített függvények).
Egy másik megoldás a rekordok feldarabolására, hogy minden mezőt
külön sorba teszünk: ekkor az FS-t a "\n" szövegre kell
beállítani. (Ez az egyszerű reguláris kifejezés csak egy új sor karakterre
illeszkedik.)
Egy praktikus példa az így elrendezett adatokra egy levelezési címeket tartalmazó lista, ahol minden bejegyzést egy üres sor választ el. Egy ilyen file, `addresses', így nézhet ki:
Jane Doe 123 Main Street Anywhere, SE 12345-6789 John Smith 456 Tree-lined Avenue Smallville, MW 98765-4321 ...
Egy egyszerű program a file feldolgozására:
# addrs.awk --- simple mailing list program
# Records are separated by blank lines.
# Each line is one field.
BEGIN { RS = "" ; FS = "\n" }
{
print "Name is:", $1
print "Address is:", $2
print "City and State are:", $3
print ""
}
A programot futtatva ezt az eredményt kapjuk:
$ awk -f addrs.awk addresses -| Name is: Jane Doe -| Address is: 123 Main Street -| City and State are: Anywhere, SE 12345-6789 -| -| Name is: John Smith -| Address is: 456 Tree-lined Avenue -| City and State are: Smallville, MW 98765-4321 -| ...
Egy másik programot is bemutatunk a címlisták feldolgozására egy későbbi fejezetben, see section Printing Mailing Labels.
Az alábbi táblázat összefoglalja, hogy a rekordok hogyan lesznek
feldarabolva az RS értékétől függően (a `==' egyenlőséget
jelent):
RS == "\n"
RS == egy bármilyen karakter
RS == ""
FS-től függetlenül.
Kezdő és záró üres sorokat a file-ban nem veszi figyelembe.
RS == regexp
A gawk mindig beállítja az RT változót az RS-re
illeszkedő szövegre.
getline-al
Eddig a bemenetet az awk program számára vagy a szabványos
bemenetről (általában a terminálról, néha egy másik program kimenetéből)
vagy a parancssorban megadott file-okból olvastuk be. Az awk
nyelvben a getline beépített függvénnyel lehet
explicit módon a bemenet olvasását irányítani.
getline bemutatása
A getline függvény hasznos lehet sokféleképpen, de kezdő
felhasználóknak nem ajánlott. Azért itt tárgyaljuk, mivel minden
a bemenettel kapcsolatos dolgot ebben a fejezetben tárgyalunk.
A getline függvény bemutatására használt példákban előfordul olyan
programozási megoldás, amit eddig még nem tárgyaltunk, így ezt a részt
ajánlott újra elolvasni miután végigolvastad, és már jól
ismered a könyvet.
A getline visszatérési értéke egy, ha sikeresen beolvasott egy
rekordot és zérus ha elérte a bemenet végét. Ha valami hiba volt
az olvasás során, például a file nem érhető el, akkor a visszatérési
értéke -1. Ebben az esetben a gawk az ERRNO
változót beállítja egy, a hibát leíró szövegre.
Az alábbi példákban a command egy shell parancsot helyettesít.
getline használata argumentum nélkül
Az argumentum nélkül meghívott getline függvény az aktuális file-ból
olvas be rekordot. Csak annyit csinál, hogy beolvassa a következő
rekordot és feldarabolja mezőkre. Ez a viselkedés akkor lehet hasznos,
ha befejezted az aktuális rekord feldolgozását és közvetlenül utána
valamilyen speciális műveletet akarsz a következő rekordon
elvégezni. Itt egy példa:
awk '{
if ((t = index($0, "/*")) != 0) {
# value will be "" if t is 1
tmp = substr($0, 1, t - 1)
u = index(substr($0, t + 2), "*/")
while (u == 0) {
if (getline <= 0) {
m = "unexpected EOF or error"
m = (m ": " ERRNO)
print m > "/dev/stderr"
exit
}
t = -1
u = index($0, "*/")
}
# substr expression will be "" if */
# occurred at end of line
$0 = tmp substr($0, t + u + 3)
}
print $0
}'
Ez az program kitöröl minden a C programozási nyelvben szokásos megjegyzést, `/* ... */', a bemenetből. Ha a `print $0' kifejezést lecseréled valamilyen másik kifejezésre, akkor bonyolultabb műveletet is végezhetsz a megjegyzésektől mentes bemeneten, pl. reguláris kifejezésekkel változókat, stb. kereshetsz. A programnak van egy apró hibája -- ugyanis nem működik, ha egy megjegyzés az adott sorban végződik és egy másik megjegyzés ugyanabban a sorban kezdődik.
Ha a getline függvényt így használod, akkor frissíti az NF
(mezők száma; see section Mezők elérése), az NR
(az eddig beolvasott rekordok száma;
see section Hogyan történik a feldarabolás rekordokra), az FNR
(az ebből a file-ból beolvasott rekordok száma) és a $0
változó értékét.
Megjegyzés: A getline az új $0 értéket használja
bármilyen további szabályban megadott mintaillesztésre. A $0
azon értéke, ami az aktuális szabályt aktiválta elveszett (s.s.).
Ezzel ellentétben a next kifejezés beolvassa a következő rekordot
és a feldolgozást az első szabálytól kezdi.
See section A next kifejezés.
getline-alA `getline var' kifejezés beolvassa a következő rekordot és a var változóban tárolja. Semmilyen más feldolgozás nem történik.
Például tegyük fel, hogy a következő sor a bemeneten egy megjegyzés
vagy egy speciális szöveg és be akarod olvasni és tárolni, de
egy szabályt sem akarsz aktiválni. A getline ezen formája ezt
teszi lehetővé, ráadásul az awk fő
rekord-beolvasás-és-minden-szabály-ellenőrzése ciklus az így beolvasott
rekordot soha nem látja.
Az alábbi példa a bemenet minden második sorát felcseréli az előzővel, tehát ha a bemenet:
wan tew free phore
akkor az eredmény:
tew wan phore free
A program:
awk '{
if ((getline tmp) > 0) {
print tmp
print $0
} else
print $0
}'
Ha a getline függvényt így használod, akkor csak az NR
és az FNR (és természetesen a var) változók értéke
változik meg. A beolvasott rekordot nem darabolja fel mezőkre, így sem a
a mezők sem a $0 és az NF értéke nem változik meg.
getline-alA `getline < file' kifejezés beolvassa a következő rekordot a file-ból. Itt a file egy szöveg értékű kifejezés kell legyen, ami a file nevét adja meg. A `< file' kifejezést átirányításnak is szokták nevezni, mivel azt adja meg, hogy a bemenet valahonnan máshonnan (más irányból) jöjjön.
Például az alábbi program egy új rekordot olvas be a `secondary.input' file-ból, ha az első mező értéke tíz az aktuális rekordban.
awk '{
if ($1 == 10) {
getline < "secondary.input"
print
} else
print
}'
Mivel nem a fő bemenetet használja, az NR és az FNR
változók értéke nem változik meg, de az újonnan beolvasott rekordot
feldarabolja mezőkre a normális módon, így a $0 és az NF
is megváltozik.
A POSIX szabvány szerint a `getline < expression' nem
egyértelmű, ha a kifejezés tartalmaz a `$'-on kívül egyéb
operátort; például a `getline < dir "/" file' nem egyértelmű,
mert az összefűzés operátor nincs zárójelek között. Ha több awk
implementációval is használni szeretnéd a programodat, akkor a
`getline < (dir "/" file)' kifejezést érdemes használni.
getline-alA `getline var < file' kifejezés beolvassa a következő rekordot a file-ból és a var változóban tárolja. Mint előbb, a file itt is egy szöveg értékű kifejezés kell legyen, ami a file nevét adja meg.
Ebben az esetben egyetlen beépített változó tartalma sem változik meg és a rekord nem lesz feldarabolva mezőkre. Egyedül a var változó kap új értéket.
Például az alábbi program a bemeneti file minden sorát kinyomtatja, kivéve azokat a rekordokat amelyek a `@include filename' kifejezést tartalmazzák. Ezeket a rekordokat a filename file tartalmával helyettesíti.
awk '{
if (NF == 2 && $1 == "@include") {
while ((getline line < $2) > 0)
print line
close($2)
} else
print
}'
Érdemes megfigyelni, hogy az extra file neve nincs beépítve a programba, hanem a második mezőből olvassuk ki.
A close függvény biztosítja, hogy ha két azonos `@include'
sor van a file-ban, akkor a megadott file tartalma kétszer lesz
kinyomtatva.
See section Bemeneti és kimeneti file-ok és csövek lezárása..
A program egyik hibája, hogy beágyazott `@include' kifejezéseket nem tud feldolgozni (egy `@include' egy másik `@include' kifejezéssel megadott file-ban), mint ahogy egy igazi macro feldolgozó programnak kellene. See section An Easy Way to Use Library Functions, amely tartalmaz egy megoldást beágyazott `@include' kifejezésekre.
getline-al
Egy másik program kimenetét átirányíthatod a getline-ba, a
`command | getline' kifejezéssel. Ebben az esetben
a command mint egy shell parancs fog lefutni, és a kimenetét
az awk fogja használni, mint bemenetet. A getline
egyszerre csak egy rekordot olvas be a csőből.
Például az alábbi program a bemenetét a kimenetre másolja, kivéve azokat a sorokat amelyek egy `@execute' szöveget tartalmaznak, amikor is a rekord többi részét mint shell parancs hajtja végre és az eredményt nyomtatja ki.
awk '{
if ($1 == "@execute") {
tmp = substr($0, 10)
while ((tmp | getline) > 0)
print
close(tmp)
} else
print
}'
A close függvény biztosítja, hogy ha két azonos `@include'
sor van a file-ban, akkor a megadott file tartalma kétszer lesz
kinyomtatva.
See section Bemeneti és kimeneti file-ok és csövek lezárása..
Tehát, ha ez a bemenet:
foo bar baz @execute who bletch
a program ezt az eredményt produkálja:
foo bar baz arnold ttyv0 Jul 13 14:22 miriam ttyp0 Jul 13 14:23 (murphy:0) bill ttyp1 Jul 13 14:23 (murphy:0) bletch
A program végrehajtotta a who parancsot és annak eredményét
nyomtatja ki. (Ha kipróbálod a programot, valószínű, hogy más eredményt
fogsz kapni, mivel azokat a felhasználókat fogja kinyomtatni akik be
vannak jelentkezve a rendszeren.)
A getline ilyen használata esetén a bemenetet feldarabolja, beállítja
az NF értékét és a $0-t újraértékeli. Az
NR és az FNR értéke nem változik meg.
A POSIX szabvány szerint a `expression | getline' nem
egyértelmű, ha a kifejezés tartalmaz a `$'-on kívúl egyéb
operátort; például a `"echo " "date" | getline' nem egyértelmű,
mert az összefűzés operátor nincs zárójelek között. Ha több awk
implementációval is használni szeretnéd a programodat, akkor a
`("echo " "date") | getline' kifejezést érdemes használni.
(A gawk helyesen kezeli ezt az esetet, de nem érdemes ebben
bízni. A zárójelekkel egyébként is jobban olvasható, hogy mi is
történik.)
getline-al
Ha a `command | getline var' kifejezést használod,
akkor a command kimenete a getline-ba lesz
átirányítva majd a var változót is beállítja. Például az
alábbi program beolvassa a mai dátumot és a pontos időt a
current_time változóba a date segédprogram
segítségével, majd kinyomtatja:
awk 'BEGIN {
"date" | getline current_time
close("date")
print "Report printed on " current_time
}'
Ebben az esetben egyetlen beépített változó sem változik meg és a rekordot sem darabolja fel mezőkre.
getline változatairól
A getline használata esetén bár a $0 és az NF
értéke lehet hogy megváltozik, de a beolvasott
rekordot nem teszteli minden mintával az awk programban,
ami normális esetben történne. Ugyanakkor a beolvasást követő
szabályokban az új rekordot használja.
Sok awk implementáció egyre korlátozza az egyszerre megnyitható
csövek számát. A gawk-ban nincs ilyen korlátozás, annyi
csövet nyithatsz meg, amennyit az operációs rendszer megenged.
A getline-nak egy érdekes mellékhatása van, ha a BEGIN
szabályon belül használjuk. Mivel az átirányítás nélküli getline
a parancssorban megadott file-okból olvas, az első getline
kifejezés beállítja a FILENAME változót is. Általában a
FILENAME-nek nincs értéke a BEGIN szabályon belül, mivel
a file-ok feldolgozása még nem kezdődött meg (s.s.).
(See section A BEGIN és az END speciális minták,
és see section Információt hordozó beépített változók.)
Az alábbi táblázat összefoglalja a getline hat lehetséges
használati módját, illetve megadja, hogy mely változók értéke változik meg.
getline
$0, NF, FNR és az NR változókat.
getline var
FNR és az NR változókat.
getline < file
$0 és az NF változókat.
getline var < file
command | getline
$0 és az NF változókat.
command | getline var
Az egyik leggyakoribb tevékenység a nyomtatás, vagyis a teljes bemenet
vagy csak a bemenet egy részének a kinyomtatása. Egyszerű nyomtatás
esetén a print-et érdemes használni. Bonyolultabb formázásra és
nyomtatásra a printf alkalmas. Mindkét lehetőséget ebben a fejezetben
tárgyaljuk.
print kifejezés
A print kifejezés egyszerű, szabványos formázással nyomtatja ki
az eredményt, csak meg kell adni a szövegeket, számokat egy
vesszővel elválasztott listában. A nyomtatás során szóközök választják
el a megadott lista elemeit, majd az egészet egy új sor karakter zárja.
Egy kifejezés valahogy így néz ki:
print elem1, elem2, ...
Ha akarod, akkor a teljes listát zárójelek közé teheted. A zárójel kötelező,
ha a lista bármely eleme használja a `>' karaktert (összehasonlító
operátor), mivel összekeverhető az átirányítás operátorral
(see section A print és a printf kimenetének átirányítása).
A kinyomtatandó elemek lehetnek szöveg vagy szám konstansok, az aktuális
rekord mezői (mint a $1), változók vagy bármilyen awk
kifejezés. A numerikus kifejezéseket előbb szöveggé konvertálja, és csak
utána nyomtatja ki.
A print kifejezésnek teljesen szabadon megadhatod, hogy mit
nyomtasson ki, de két kivételtől eltekintve nem tudod befolyásolni,
hogy hogyan jelenítse meg az eredményt -- hány számjegyet,
exponenciális vagy lebegőpontos formát használjon és így tovább.
(A kivételeket két bekezdésben mutatjuk be,
see section Kimeneti elválasztó és
a section A numerikus adatok formátumának megadása a print esetén.)
A megjelenítési forma megadásához a printf kifejezést kell
használni
(see section Nyomtatás a printf kifejezéssel).
Az önmagában álló `print' kifejezés megegyezik a `print $0'
kifejezéssel: kinyomtatja a teljes rekordot. Egy üres sor nyomtatásához
a `print ""' kifejezést kell használni, ahol a "" egy üres szöveg.
Egy adott szöveget szöveg konstansként érdemes kinyomtatni. Ha elfelejted
megadni a macskakörmöket, akkor a szavak mint awk kifejezések lesznek
kezelve, és a program valószínűleg hibát fog jelezni. Emlékezz arra is, hogy
minden elem közé egy extra szóközt is nyomtat.
Minden print kifejezés legalább egy sort nyomtat, persze
lehet hogy többet is. Ha egy szöveg tartalmazza az új sor karaktert,
akkor azt is kinyomtatja, vagyis a szöveg többi részét egy új sorba
nyomtatja ki. Egy print kifejezés tetszőleges számú sort
nyomtathat ki.
print kifejezésselAz első példa bemutatja a beágyazott új sor karakterek nyomtatását (a `\n' escape szekvencia az új sor karaktert reprezentálja; see section Escape szekvenciák):
$ awk 'BEGIN { print "line one\nline two\nline three" }'
-| line one
-| line two
-| line three
A második példa minden bemeneti rekordból kinyomtatja az első két mezőt egy szóközzel elválasztva:
$ awk '{ print $1, $2 }' inventory-shipped
-| Jan 13
-| Feb 15
-| Mar 15
...
Gyakori hiba a print használata során, hogy nem teszünk vesszőt
az elemek közé. Ennek az a hatása, hogy közvetlenül egymás után írja ki
az elemeket, mivel ez az írásmód az awk-ban az összefűzést jelenti.
Tehát ugyanaz a példa vesszők nélkül:
$ awk '{ print $1 $2 }' inventory-shipped
-| Jan13
-| Feb15
-| Mar15
...
Ha valaki nem ismeri az `inventory-shipped' file tartalmát, akkor
a fenti példák eredményei nem mondanak túl sokat. Ilyenkor jól jöhet
egy fejléc, jelezve, hogy az első mező a hónap ($1) a második mező
($2) pedig a zöld rekeszek száma. A fejlécet a BEGIN
mintán belül nyomtatjuk ki, hogy csak egyszer jelenjen meg
(see section A BEGIN és az END speciális minták):
awk 'BEGIN { print "Hónap Rekesz"
print "----- ------" }
{ print $1, $2 }' inventory-shipped
Kitalálod, hogy mi fog történni? A program futtatása után ezt kapjuk:
Month Crates ----- ------ Jan 13 Feb 15 Mar 15 ...
Az adatok és a fejléc nem kerül egy oszlopba! A probléma persze megoldható ha megfelelő számú szóközt teszünk a mezők közé:
awk 'BEGIN { print "Hónap Rekesz"
print "----- ------" }
{ print $1, " ", $2 }' inventory-shipped
Könnyű elképzelni, hogy több oszlop igazítása ezzel a módszerrel elég
bonyolult lehet. Kettő vagy három oszlop esetén a szóközök még könnyen
kiszámolhatók, de több oszlop esetén könnyű eltéveszteni. Ezért találták
ki a printf kifejezést
(see section Nyomtatás a printf kifejezéssel),
aminek az egyik specialitása az oszlopok igazítása.
Mellékesen, egy print vagy printf kifejezés folytatható a
következő sorban, ha a vessző után egy új sor karakter áll
(see section awk kifejezések és sorok).
Ahogy azt korábban leírtuk, a print kifejezés argumentumait
vessző választja el. Ennek hatására a kinyomtatás során az elemek között egy
szóközt nyomtat. Ennek persze nem kell így lennie; a szóköz csak az
alapbeállítás. Bármilyen karaktersorozat megadható, mint kimeneti
mezőelválasztó, az OFS beépített változónak. A változó
kezdeti értéke a " " szöveg, egy szóköz.
Egy teljes print kifejezés kimenete egy kimeneti rekord.
Minden print kifejezés kinyomtat egy kimeneti rekordot, majd
egy szöveget amit úgy hívnak, hogy kimeneti rekordelválasztó, az
ORS beépített változóban megadott értéket. Az ORS
kezdeti értéke a "\n", az új sor karakter; így alapesetben
minden print kifejezés egy új sorral zárja a rekordot.
Természetesen megváltoztathatod, hogy a kimeneti rekordok és mezők hogyan
legyenek elválasztva, ha új értéket adsz az OFS és/vagy ORS
változóknak. Ezt általában a BEGIN szabályban érdemes megtenni,
(see section A BEGIN és az END speciális minták),
a bemenet feldolgozásának megkezdése előtt. A változókat beállíthatod
a parancssorból is a bemeneti file-ok előtt vagy a `-v' parancssori
opció használatával (see section Command Line Options).
Az alábbi példa kinyomtatja az első és a második mezőt egy pontos vesszővel elválasztva és a rekordok közé egy új, üres sort illesztve:
$ awk 'BEGIN { OFS = ";"; ORS = "\n\n" }
> { print $1, $2 }' BBS-list
-| aardvark;555-5553
-|
-| alpo-net;555-3412
-|
-| barfly;555-7685
...
Ha az ORS nem tartalmaz egy új sor karaktert, akkor minden kimenet
egy sorba lesz kinyomtatva, kivéve persze ha valami más explicit módon
nincs megadva az új sor karakter nyomtatása.
print esetén
Amikor a print kifejezéssel egy számot nyomtatunk ki, az
awk átalakítja a számot szöveggé, majd ezt a szöveget nyomtatja
ki. Az awk az sprintf függvényt használja a
konvertáláshoz
(see section Szövegmanipuláló beépített függvények).
Egyelőre elég annyit elmondani, hogy az sprintf úgynevezett
formátumleírót használ, ami megadja, hogy a szám (vagy szöveg)
hogyan fog megjelenni. A különböző formátumokat részletesen tárgyaljuk
egy későbbi alfejezetben, section Formátumleíró betűk.
Az OFMT beépített változó tartalmazza az alap formátumleírót,
amit a print használ a számok szöveggé konvertálása során. Az
OFMT alapértéke "%.6g". Ha más értéket adunk meg
az OFMT-nek, akkor szabályozható, hogy a számokat hogyan
nyomtassa ki a print. Egy rövid példa:
$ awk 'BEGIN {
> OFMT = "%.0f" # minden számot integerként nyomtat ki
> print 17.23 }'
-| 17
A POSIX szabvány szerint az awk viselkedése nem definiált, ha az
OFMT nem lebegőpontos formátumleírót tartalmaz (s.s.).
printf kifejezéssel
Ha a print lehetőségeinél jobban akarod kontrollálni a
nyomtatási formátumot, akkor a printf kifejezést használd.
A printf -el megadhatod a nyomtatási szélességet minden elemre
és a számokat különböző formátumban (különöző számrendszerben,
exponenciális vagy lebegőpontos alakban, megadott számjegy pontossággal)
nyomtathatod ki. Mindezt egy formátumszöveg megadásával érheted el.
printf bemutatása
A printf kifejezést így kell használni:
printf formátum, elem1, elem2, ...
Az argumentumokat zárójelek közé is teheted. A zárójel kötelező, ha
bármelyik elem a `>' összehasonlító operátort tartalmazza, mert
az awk összekeverheti az átirányítás operátorral
(see section A print és a printf kimenetének átirányítása).
A printf és a print közötti különbség a format
argumentum. Ez egy szöveg értékű kifejezés; megadja, hogy a többi
argumentumot hogyan nyomtassa ki. A kifejezést formátumszövegnek
hívják.
A formátumszöveg nagyon hasonló az ANSI C-ben használt printf
függvény argumentumához. A format kifejezés jelentős része
egyszerű szöveg, amit csak ki kell nyomtatni. A szöveg között vannak
elszórva a formátumleírók. Minden elemhez tartozik egy. A formátum
szöveg veszi a következő argumentumot, és az adott helyen kinyomtatja.
A printf kifejezés nem kezd új sort a nyomtatás végén automatikusan.
Csak annyit és azt nyomtat ki, amit a formátumszöveg megad, így ha egy
új sor karakter is kell a szöveg végére, akkor azt explicit módon kell
megadni. Az OFS és az ORS változóknak nincs hatása a
printf kifejezésre, így:
BEGIN {
ORS = "\nOUCH!\n"; OFS = "!"
msg = "Don't Panic!"; printf "%s\n", msg
}
A program még mindig csak annyit nyomtat ki, hogy `Don't Panic!'.
Egy formátumleíró a `%' karakterrel kezdődik és egy betűvel végződik. (Ha csak a `%' karaktert akarod kinyomtatni, akkor a `%%' karaktersorozatot kell használni.) A formátumleíró betű határozza meg a kinyomtatandó adat típusát. A leíró többi része csak módosítja a megjelenést, mint például a nyomtatási szélességet.
Itt egy lista a formátumleíró betűkről:
c
d
i
e
E
printf "%4.3e\n", 1950`1.950e+03' -t fog kinyomtatni, négy értékes jegyre, amiből három számjegy a tizedes pont után áll. A `4.3' egy leíró módosító és később tárgyaljuk. A `%E' egy nagy `E' betűt használ a számban a kis `e' betű helyett.
f
printf "%4.3f", 1950`1950.000' -t jeleníti meg négy értékes jegyre, amiből három számjegy a tizedes pont után áll. A `4.3' egy leíró módosító és később tárgyaljuk.
g
G
o
s
x
X
%
printf argumentumait, és minden
leíró módosítót figyelmen kívül hagy.
Amikor az egész (integer) formátumleíró betűt használjuk
egy olyan értéknél, ami
nem reprezentálható a C long típussal, akkor a gawk
automatikusan a `%g' formázási módba kapcsol. Más awk
verziók valótlan értéket nyomtathatnak, vagy valami furcsa dolgot
csinálhatnak ebben az esetben (s.s.).
printf formátum módosítóiA formátum leírók módosító komponenseket is tartalmazhatnak, amelyek meghatározzák, hogy az elemből mennyi jelenik meg. A módosító komponenseket a `%' karakter és a formátumleíró betű között kell elhelyezni. Az alábbi példákban a "*" jelet használjuk a szóközök helyén a kimenetben. A módosítók az alábbi felsorolás sorrendjében jelenhetnek meg:
-
printf "%-4s", "foo"ezt adja eredményül: `foo*'.
szóköz
+
#
0
szélesség
printf "%4s", "foo"eredménye: `*foo'. A szélesség a minimumot adja meg és nem a maximumot. Így ha a kinyomtatandó elem szélesebb mint a megadott érték ettől függetlenül az egész elemet kinyomtatja, például:
printf "%4s", "foobar"eredménye: `foobar'. A szélesség előtti mínusz jel hatására az extra szóközöket a jobb oldalra rakja.
.pontosság
printf "%.4s", "foobar"eredménye: `foob'.
A C programozási nyelv printf kifejezésében használható
dinamikus szélesség és pontosság meghatározás ("%*.*s")
az awk-ban is engedélyezett. Ebben az esetben nem a formátum
szövegben kell megadni a szélesség és/vagy pontosság
értékét, hanem mint argumentum kell átadni a kifejezésnek, például:
w = 5 p = 3 s = "abcdefg" printf "%*.*s\n", w, p, s
pontosan ugyanaz, mint
s = "abcdefg" printf "%5.3s\n", s
Mindkét program a `**abc' kifejezést nyomtatja ki.
Az awk korábbi verziói nem támogatták ezt a lehetőséget. Ha ilyen
awk-ot kell használnod, akkor az összefűzés operátorral szimulálhatod
a dinamikus módot:
w = 5 p = 3 s = "abcdefg" printf "%" w "." p "s\n", s
Bár nem egyszerű olvasni ezt a formát, de működik.
A C programozók már hozzászokhattak a `l' és `h' megadásához
a printf formátumszövegében, de az awk-ben ezek
nem használhatók. A legtöbb awk implementáció csendben
figyelmen kívül hagyja ezeket a módosítókat. Ha a `--lint' opció
meg van adva a parancssorban (see section Command Line Options),
akkor a gawk figyelmeztet ezen módosítók használata esetén.
Ha a `--posix' opció van megadva, akkor használatuk végzetes
hiba, a program azonnal leáll.
printf példák
Ez a példa egy igazított táblázatot nyomtat ki a printf segítségével:
awk '{ printf "%-10s %s\n", $1, $2 }' BBS-list
kinyomtatja a `BBS-list' file-ban található rendszerek nevét ($1)
egy tíz karakter szélességű mezőbe, balra igazítva. Ezenkívül kinyomtatja
a telefonszámot ($2) is. Az alábbi két oszlopból álló táblázat az
eredmény:
$ awk '{ printf "%-10s %s\n", $1, $2 }' BBS-list
-| aardvark 555-5553
-| alpo-net 555-3412
-| barfly 555-7685
-| bites 555-1675
-| camelot 555-0542
-| core 555-2912
-| fooey 555-1234
-| foot 555-6699
-| macfoo 555-6480
-| sdace 555-3430
-| sabafoo 555-2127
Észrevetted, hogy a telefonszámot nem mint számot nyomtattuk ki? A telefonszámokat mint szöveg kellett kinyomtatni, mivel szerepel egy mínusz jel a számban. Ha mint számot nyomtatjuk ki, akkor csak az első három számjegyet kapnánk meg eredményül, `555'.
A telefonszám formátumánál nem adtunk meg szélességet, mivel az utolsó elem a sorban, így nem akarunk szóközöket utána.
A táblázatot szebbé tehetjük, ha egy fejlécet adunk az oszlopok tetejéhez.
Ehhez a BEGIN mintát kell alkalmazni
(see section A BEGIN és az END speciális minták),
mivel így csak egyszer, az awk program legelején nyomtatja ki a
fejlécet:
awk 'BEGIN { print "Név Szám"
print "--- ----" }
{ printf "%-10s %s\n", $1, $2 }' BBS-list
Észrevetted, hogy a print és printf kifejezéseket is használtunk
a fenti példában? Használhatunk csak printf kifejezést is:
awk 'BEGIN { printf "%-10s %s\n", "Név", "Szám"
printf "%-10s %s\n", "---", "----" }
{ printf "%-10s %s\n", $1, $2 }' BBS-list
Mivel ugyanazt a formátumot használtuk a fejléc és az adatok nyomtatására, így biztosak lehetünk benne, hogy megfelelően lesznek igazítva.
Ha hangsúlyozni akarjuk, hogy ugyanazt a formátumot kell használni mindegyik esetben, akkor a formátumot egy változóban is tárolhatjuk:
awk 'BEGIN { format = "%-10s %s\n"
printf format, "Név", "Szám"
printf format, "---", "----" }
{ printf format, $1, $2 }' BBS-list
Most már tudod, hogyan kellett volna használni a printf kifejezést
az `inventory-shipped' file-ból nyert táblázat nyomtatásánál
(see section A print kifejezés). Meg tudod csinálni?
print és a printf kimenetének átirányítása
Eddig csak azokkal az esetekkel foglalkoztunk, amikor a nyomtatás a
szabványos kimeneten, általában a terminálon jelent meg. A print
és a printf is képes a kimenetet átirányítani.
Az átirányítást a print vagy a printf kifejezés után
kell írni és a formája ugyanolyan, mint a shell-ben használt átirányítás.
Az átirányításnak három formája van: kimenet egy file-ba, kimenet
hozzáfűzése egy file-hoz és kimenet átirányítása egy csővön (pipe-on)
keresztül egy másik parancsba. A print kifejezéssel mutatjuk be
az átirányítás használatát, de ugyanígy minden érvényes a printf
kifejezésre is.
print elemek > output-file
awk program a BBS neveket a `name-list'
file-ba írja, míg a telefonszámokat a `phone-list' file-ba. Minden
név vagy szám egy külön sorba kerül.
$ awk '{ print $2 > "phone-list"
> print $1 > "name-list" }' BBS-list
$ cat phone-list
-| 555-5553
-| 555-3412
...
$ cat name-list
-| aardvark
-| alpo-net
...
print elemek >> output-file
awk létrehozza.
print elemek | parancs
awk megnyitja a csövet
a parancs felé, majd az elemeket kiírja a csőbe.
A parancs átirányítási argumentum egy awk kifejezés. Az értékét
egy szöveggé alakítja, ami megadja a végrehajtandó shell parancsot.
Az alábbi példa két file-t hoz létre, a BBS nevek egy rendezetlen és egy
ábécé sorrendben visszafelé rendezett listáját:
awk '{ print $1 > "names.unsorted"
command = "sort -r > names.sorted"
print $1 | command }' BBS-list
A rendezetlen listát egy normál átirányítással írjuk ki, míg a rendezést
a sort segédprogram végzi el, ami egy csövön keresztül kapja az
adatokat.
A következő példa átirányítással küld egy üzenet a `bug-system'
levelezési listára. Ilyen program hasznos lehet ha a rendszer adminisztrációra
használt awk program hibát talál valahol.
report = "mail bug-system"
print "Awk script failed:", $0 | report
m = ("at record number " FNR " of " FILENAME)
print m | report
close(report)
Az üzenetet összefűzéssel készíti el, és az m változóban tárolja, majd
egy csövön keresztül a mail programnak küldi tovább.
A close függvény alkalmazása fontos, mivel ez lezárja a kimeneti
csövet.
See section Bemeneti és kimeneti file-ok és csövek lezárása..
A példa azt is bemutatja, hogy hogyan használhatunk egy változót a file
vagy a parancs reprezentálására. A változók azért is hasznosak, mert
egyébként minden alkalommal pontosan ugyanúgy kellene leírni a hosszú
szövegeket.
A `>', `>>' vagy `|' átirányítás arra kéri a rendszert, hogy nyisson meg egy file-t vagy csövet, de csak akkor, ha az adott file-t vagy parancsot még nem használta a program vagy ha az utolsó használat végén le lett zárva.
Mint ahogy azt már korábban írtuk
(see section Összefoglaló a getline változatairól),
néhány awk implementáció csak egy cső (pipe) megnyitását engedélyezi.
A gawk-ban nincs ilyen korlátozás, annyi csövet lehet megnyitni,
amennyit az operációs rendszer engedélyez.
gawk-banHagyományosan egy program összesen három karakterfolyamot (stream-et) használ beolvasásra és kiírásra. Ezek a szabványos bemenet, szabványos kimenet és a szabványos hibakimenet, amelyek általában a terminálodhoz kapcsolódnak, de gyakran a shell átirányítja őket a `<', `<<', `>', `>>', `>&' és a `|' operátorok valamelyikével. A szabványos hibakimenet a hibaüzeneteket jeleníti meg; azért van két különböző karakterfolyam, a szabványos kimenet és a szabványos hibakimenet, mert így külön-külön lehet őket átirányítani.
Néhány awk implementáció esetén az egyetlen lehetőség egy awk
programban a hibaüzenetek megjelenítésére a szabványos hibakimeneten
keresztül az alábbi módszer:
print "Serious error detected!" | "cat 1>&2"
Ez úgy működik, hogy egy csövet nyit meg egy olyan shell parancs felé,
amelyik el tudja érni a szabványos hibakimenetet. A szabványos hibakimenet
értékét az awk-tól veszi át. Ez a megoldás nem elegáns és
nem is hatékony, mivel egy másik programot kell indítani. Így az emberek
jelentős része nem ezt a módszert használja, helyette a hibaüzeneteket a
terminálra küldi, például így:
print "Serious error detected!" > "/dev/tty"
Általában ennek ugyanaz a hatása, de nem mindig: habár a terminál a
szabványos hibakimenet az esetek nagy részében, de át lehet irányítani,
és ebben az esetben a terminálra írás nem jó megoldás. Ráadásul ha az
awk a háttérben fut, akkor nincs is terminál hozzárendelve, és a
`/dev/tty' megnyitása nem lesz sikeres.
A gawk speciális file neveket biztosít a három alap karakterfolyam
eléréséhez. Ha a kimenetet vagy a bemenetet átirányítjuk, és a file neve
megegyezik valamelyik speciális névvel, akkor a gawk az adott
karakterfolyamot fogja használni.
awk-ot elindító programnak kell megnyitnia (általában a shell).
Ha csak nem bravúroskodsz, akkor csak a 0, 1 és a 2 file leírók
állnak rendelkezésre.
A `/dev/stdin', `/dev/stdout' és `/dev/stderr' file-ok megegyeznek a `/dev/fd/0', `/dev/fd/1' és `/dev/fd/2' file-okkal sorrendben, de a nevük kifejezőbb.
A helyes módszer egy hiba megjelenítésére egy gawk programból
a `/dev/stderr' használatával:
print "Serious error detected!" > "/dev/stderr"
A gawk olyan speciális file-ok elérését is biztosítja, amelyek
az éppen futó gawk-ról adnak információt. Minden ilyen "file"
csak egy rekordnyi információt tartalmaz. Ha többször akarod kiolvasni
az értékét, akkor előbb le kell zárni a close függvénnyel
(see section Bemeneti és kimeneti file-ok és csövek lezárása.).
A file-ok:
$1
getuid rendszer függvény visszatérési értéke (a valódi felhasználói
azonosító, ID).
$2
geteuid rendszer függvény visszatérési értéke (az effektív felhasználói
azonosító).
$3
getgid rendszer függvény visszatérési értéke (a felhasználó valódi
csoport azonosítója).
$4
getegid rendszer függvény visszatérési értéke (a felhasználó effektív
csoport azonosítója).
getgroups rendszer függvény által visszaadott
csoport azonosítókat tartalmazza. (Nem minden rendszer támogatja, hogy egy
felhasználó több csoportba tartozhat.)
Ezeket a speciális file-okat a parancssorban is lehet használni mint adat
file-ok vagy az awk programon belül átirányítással, de nem
használhatók mint program file a `-f' opcióval.
"Compatibility" módban (see section Command Line Options)
a gawk nem ismeri fel ezeket a speciális file-okat.
Figyelem: Ha a rendszereden nincs `/dev/fd' könyvtár
(vagy bármely más fent megadott speciális file), akkor a gawk maga
fogja értelmezni a megadott file nevet. Például a `/dev/fd/4'
mint kimenet használata esetén a program a 4-es file leíró által megadott
file-ba fog írni és nem egy olyan file leíróba ami a 4-es file leíró
másolata. Általában ez nem annyira érdekes; de fontos, hogy ne
zárjuk le a 0, 1 és 2 -es file leírókat, ugyanis ebben az esetben
az awk viselkedése megjósolhatatlan.
A futó awk programról információt adó file-ok lehet,
hogy nem lesznek benne a gawk későbbi verzióiban,
see section Probable Future Extensions.
Ha többször ugyanazt a file nevet vagy shell parancsot használjuk a
getline-al (see section Explicit beolvasás getline-al)
egy awk programon belül, a file-t csak az első alkalommal nyitja meg
(a parancsot csak az első alkalommal hajtja végre). Ezzel egy időben
beolvassa az első rekordot. A következő alkalommal, amikor ugyanazt
a file-t vagy shell parancsot hasnáljuk a getline-al, a következő
rekordot olvassa be.
Ugyanez érvényes a csövekre is ha írunk bele; az adott file-ra vagy
parancsra emlékszik az awk és az első alkalom után mindig
ugyanabba a file-ba írja vagy ugyanannak a parancsnak küldi a kimenetet.
A file vagy cső addig marad nyitva, amíg az awk ki nem lép.
Ez persze azt jelenti, hogy ha ugyanazt a file-t többször szeretnéd
beolvasni az elejétől vagy ugyanazt a shell parancsot szeretnéd többször
lefuttatni, akkor extra lépéseket kell tenned. A close függvényt
kell használni:
close(filenév)
vagy
close(parancs)
A filenév vagy parancs argumentum bármilyen kifejezés lehet, de az értéke pontosan ugyanaz kell legyen mint amit a megnyitásnál használtunk (a szóközök és egyéb "extra" karakterek is fontosak). Például ha egy csövet így nyitsz meg:
"sort -r names" | getline foo
akkor bezárni így kell:
close("sort -r names")
Miután ez a függvény lefutott a következő getline, print
vagy printf kifejezésnél ugyanazt a file-t újra megnyitja vagy
ugyanazt a parancsot újra lefuttatja.
Mivel a lezárásnál ugyanolyan értékű kifejezést kell használni mint a megnyitásnál ezért érdemes a file nevét vagy a parancsot egy változóban tárolni. Az előbbi példa tehát így fog kinézni:
sortcom = "sort -r names" sortcom | getline foo ... close(sortcom)
Ez segít elkerülni a nehezen megtalálható "elgépelési" hibákat az
awk programodban.
Az alábbiakban leírunk néhány indokot, hogy a kimenetet miért érdemes/kell lezárni:
awk programból hozzuk létre a file-t, mint amiből
később visszaolvassuk. A létrehozás, írás után le kell zárni a file-t, így a
getline függvénnyel elölről olvasható.
awk programból több file-ba írunk. Ha nem zárjuk le
a file-okat, lehet, hogy egy idő után több file-t szeretnénk nyitva tartani
egyszerre, mint amennyit a rendszer engedélyez. Így ha egy file-al végeztünk,
zárjuk le.
mail programba irányítottuk,
akkor az aktuális üzenetet addig nem küldi el, amíg a csövet le nem
zárjuk.
mail programba. Ha több sort
is kiírunk, akkor azok ugyanabba az üzenetbe kerülnek. Ezzel ellentétben
ha a csövet lezárjuk minden sor után, akkor minden sor egy különálló
üzenet lesz.
A close függvény zérust ad vissza, ha a lezárás sikeres volt, egyébként
valamilyen zérustól különböző értéket. Ebben az esetben a gawk
beállítja az ERRNO változót egy, a hibát leíró üzenetre.
Ha több file-t próbálsz megnyitni, mint amit a rendszer engedélyez, akkor a
gawk megpróbálja a rendelkezésre álló nyitott file-okat megosztani
(multiplex), és az új file-t is megnyitni. Ez a lehetőség az operációs
rendszertől is függ; nem mindig működik, ezért jó programozási szokás
és könnyíti a hordozhatóságot ha a file-t mindig lezárod, miután már
nem használod.
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.
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.
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.(8) 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.
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).
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.)
(see section Kifejezések mint minták).
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 argumentumaként
(see section Szövegmanipuláló beépített függvények).
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 (see section 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.
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.
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. See section Értékadó kifejezések.
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.
Bármelyik awk változónak kezdő érték adható a parancssorban
az awk parancssori argumentumai között.
(see section Other Command Line Arguments). Az értékadás
formája:
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.
(See section Command Line Options, további információk a `-v'
opcióról.)
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
(see section Az ARGC és az ARGV változók használata).
Az awk a parancssori értékadásnál is figyelembe veszi az
escape szekvenciákat (s.s.) (see section Escape szekvenciák).
Szövegek számmá és számok szöveggé konvertálhatók ha az awk program
úgy kívánja. Például ha vagy a foo vagy a bar értéke
a `foo + bar' kifejezésben szöveg értékű, akkor az összeadás
előtt először a változó értéke átkonvertálódik számmá. Ha egy szám
jelenik meg szöveg összefűzésnél, akkor a számot átkonvertálja szöveggé, így:
two = 2; three = 3 print (two three) + 4
a program a (numerikus) 27-et fogja kinyomtatni. Először a two és
three változók numerikus értékeit átkonvertálja szöveggé és
összefűzi őket, majd az így kapott szöveget visszaalakítja számmá (23)
amihez négyet ad.
Ha valamiért egy számot mindenáron szeretnél szöveggé alakítani, akkor
hozzá kell fűzni egy üres szöveget, "". Ha egy szöveget kell
átalakítani számmá, akkor hozzá kell adni zérust.
A szöveg számmá konvertálása úgy történik, hogy a szöveg elején elhelyezkedő
értelmes numerikus kifejezést alakítja számmá:
"2.5" konvertálás után 2.5, "1e3" értéke 1000 és "25fix"
numerikus értéke 25.
Olyan szöveg, ami nem értelmezhető számként, a konvertálás után
zérus értékű lesz.
A számok szöveggé konvertálását a CONVFMT beépített awk
változó kontrollálja
(see section Beépített változók).
A számokat a sprintf függvénnyel
(see section Szövegmanipuláló beépített függvények)
alakítja át, ahol a formátum leírót a CONVFMT változó adja meg.
A CONVFMT alapértéke a "%.6g", ami legalább hat értékes
jegyre nyomtatja ki a számot. Előfordulhat, hogy néhány alkalmazás
esetén nagyobb pontossággal szeretnél konvertálni, de vedd figyelembe,
hogy a dupla (double) pontosság általában csak 16 vagy 17 értékes jegyet képes
tárolni.
Furcsa eredményt kaphatsz, ha a CONVFMT-ben nem adod meg a
sprintf függvénynek, hogy hogyan nyomtasson lebegő pontos számot.
Például ha elfelejted megadni a `%' karaktert a formátumban, akkor
minden szám ugyanarra a konstans szövegre lesz konvertálva.
Egy speciális eset, ha a szám egész, akkor a konvertálás eredményeként
kapott szöveg mindig egy egész számot fog tartalmazni, attól függetlenül, hogy
mi a CONVFMT értéke. Például:
CONVFMT = "%2.2f" a = 12 b = a ""
b értéke "12" és nem "12.00" (s.s.).
A POSIX szabvány előtt az awk az OFMT változót használta
a számok szöveggé konvertálásánál. Az OFMT azt adja meg, hogy a
print milyen formában nyomtasson ki egy számot. A CONVFMT-t
pont azért vezették be, hogy elkülönítsék a nyomtatás és a konvertálás
formáját. Mindkét változónak (a CONVFMT és a OFMT) ugyanaz
az alapértéke: "%.6g". A legtöbb esetben az öreg awk
programok viselkedése nem fog megváltozni, de érdemes fejben tartani az
OFMT ezen specialitását, ha a programodat más awk
implementációkhoz akarod igazítani. Egyébként ebben az esetben a programod
módosítása helyett azt tanácsoljuk, hogy magát a gawk-ot
fordítsd le és használd.
See section A print kifejezés, alatt további információ
található a print kifejezésről.
Az awk nyelvben a megszokott matematikai operátorokat lehet
használni, a precedenciaszabályok sem különbözőek, és pontosan úgy működnek,
ahogy az elvárható.
Az alábbi `grades' file egy osztályba járó tanulók nevét és három teszt eredményét tartalmazza (ez egy kis osztály):
Pat 100 97 58 Sandy 84 72 93 Chris 72 92 89
A program kinyomtatja a tanulók átlagát:
$ awk '{ sum = $2 + $3 + $4 ; avg = sum / 3
> print $1, avg }' grades
-| Pat 85
-| Sandy 83
-| Chris 84.3333
Az alábbi táblázat felsorolja az awk-ban használható matematikai
operátorokat:
- x
+ x
x ^ y
x ** y
x * y
x / y
awk-ban, ezért az eredmény nem
lesz egészre kerekítve: `3 / 4' eredménye 0.75.
x % y
b * int(a / b) + (a % b) == aEgy valószínűleg nem kívánatos mellékterméke a fenti definíciónak, hogy ha x negatív, akkor
x % y eredménye is
negatív lesz, így
-17 % 8 = -1Az eredmény előjele más
awk implementációkban eltérő lehet.
x + y
x - y
A maximális hordozhatóság érdekében ne használd a `**' operátort.
Akkor jó ötletnek tünt. Brian Kernighan
Csak egy szöveg operátor van: összefűzés; viszont nincs karakter ami jelölné. Az összefűzéshez egyszerűen egymás mellé kell írni a kifejezéseket, például:
$ awk '{ print "Field number one: " $1 }' BBS-list
-| Field number one: aardvark
-| Field number one: alpo-net
...
Ha nem lenne szóköz a kettőspont után, akkor az eredmény így nézne ki:
$ awk '{ print "Field number one:" $1 }' BBS-list
-| Field number one:aardvark
-| Field number one:alpo-net
...
Mivel az összefűzésnek nincs explicit operátora, gyakran zárójelek
közé kell tenni a kifejezéseket ahhoz, hogy az összefűzés valóban megtörténjen.
Például az alábbi példában nem kapcsolja össze a file és a
name tartalmát, mint ahogy azt elvárnánk:
file = "file" name = "name" print "valami" > file name
Így kell leírni helyesen:
print "valami" > (file name)
Az tanácsoljuk, hogy kivéve a legegyértelműbb helyzeteket, érdemes zárójelek közé tenni az összefűzendő kifejezéseket.
Az értékadás egy olyan kifejezés ami új értéket rendel egy változóhoz.
Például a z változónak így adjuk meg a numerikus egy értéket:
z = 1
A kifejezés kiértékelése után a z változó értéke egy lesz. A
z változó korábbi értéke elveszik, akármi is volt az.
Az értékadásnál megadhatunk szöveg értéket is. Például az alábbi
kifejezés a "this food is good" értéket tárolja el a
message változóban:
thing = "food" predicate = "good" message = "this " thing " is " predicate
(A példa mutatja a szöveg összefűzést is.)
Az `=' (egyenlőség) jel az értékadó operátor. Ez a legegyszerűbb értékadó operátor, mivel a jel jobb oldalán álló értéket változtatás nélkül tárolja a változóban.
A legtöbb operátornak (mint összeadás, összefűzés, stb) nincs más hatása csak az, hogy az adott értéket kiszámolja. Ha nincs szükséged az értékre akkor akár ne is használd az adott operátort. Az értékadó operátor ettől különböző; bár a jobb oldal kiértékelésével megkapott értékre elméletileg mondhatod, hogy nincs szükséged, de a változóban mindenképpen el fogja tárolni. Ezt mellékhatásnak hívják.
Az értékadás bal oldalán nem kötelező egy változónak állnia
(see section Változók); ez éppen lehet egy mező
(see section Mező tartalmának megváltoztatása) vagy egy
tömb eleme (see section Tömbök az awk-ban).
Ezeket lvalue-nak hívják mivel az értékadó operátor bal oldalán
állhatnak. A jobb oldali kifejezés bármilyen kifejezés lehet, ennek
az értékét tárolja egy változóban, mezőben vagy egy tömb elemben.
(Az ilyen értéket rvalue-nak hívják.)
Fontos megjegyezni, hogy a változóknak nincs állandó típusa.
A változó típusát az adja meg, hogy éppen milyen értéket tárol. A
következő program részletben a foo változónak először szám értéke van
majd az értéke szöveg lesz:
foo = 1 print foo foo = "bar" print foo
Amikor a második alkalommal a foo egy szöveg értéket kap, akkor
az előző szám értékét teljesen elfelejti.
Ha egy szöveg nem számmal kezdődik, akkor a numerikus értéke zérus. Így
az alábbi kód végrehajtása után a foo értéke öt:
foo = "a string" foo = foo + 5
(Figyelem, ha egy változónak néha szám és néha szöveg értéke van, az zavaró
lehet és rossz programozási stílus. A fenti példa azt mutatja be, hogy az
awk hogyan működik és nem azt, hogy hogyan kell programot írni!)
Az értékadás is egy kifejezés és az értéke megegyezik a jobb oldal kiértékelés utáni értékével. Így a `z = 1' mint kifejezés értéke egy. Ennek egyik következménye, hogy többszörös értékadást is lehet egymás után írni:
x = y = z = 0
eredménye, hogy mind a három változó értéke zérus lesz, mivel a
`z = 0' értéke zérus, amit az `y' változóban tárol, majd a
`y = z = 0' zérus értékét tárolja el az x változóban.
Értékadás minden olyan helyen használható, ahol kifejezés szerepelhet.
Például ez is érvényes `x != (y = 1)', ami először egyet rendel
az y-hoz, majd ellenőrzi, hogy az x értéke egy-e. Ugyanakkor
ezt a programozási stílust nehéz olvasni; az egyszer használatos programok
kivételével beágyazott értékadást nem érdemes használni.
Bár az `=' operátor nem, de más operátorok felhasználják
a változó régi értékét. Például, a `+=' operátor a változó régi
értékéhez hozzáadja a jobb oldal értékét, majd az így kapott új értéket
tárolja el a változóban. Így az alábbi kifejezés ötöt ad a foo
értékéhez:
foo += 5
ami megegyezik ezzel:
foo = foo + 5
Azt használd, amelyik jobban olvasható/érthető számodra.
Vannak olyan esetek, amikor a `+=' operátor (vagy bármilyen más értékadó operátor) nem ugyanazt csinálja mint amikor a bal oldali változó a jobb oldalon is szerepel, például:
# Köszönet Pat Rankin-nak ezért a példáért
BEGIN {
foo[rand()] += 5
for (x in foo)
print x, foo[x]
bar[rand()] = bar[rand()] + 5
for (x in bar)
print x, bar[x]
}
A bar indexei garantáltan különbözőek lesznek, mivel a rand
minden alkalommal más értékkel tér vissza. (A tömböket és a rand
függvényt eddig még nem tárgyaltuk,
see section Tömbök az awk-ban,
és lásd még section Numerikus beépített függvények,
további információkért).
Ez a példa az értékadás operátorok egy másik fontos tulajdonságát is demonstrálja: a bal oldali kifejezés csak egyszer lesz kiértékelve.
Az implementációtól függ, hogy melyik kifejezés értékelődik ki először, a jobb vagy a bal oldali kifejezés, például:
i = 1 a[i += 2] = i + 1
Az a[3] értéke kettő vagy négy is lehet.
Az alábbi táblázat összefoglalja az értékadó operátorokat. Mindegyik esetben
a jobb oldali kifejezést kiértékeli és ha szükséges számmá konvertálja az
awk.
lvalue += increment
lvalue-ban.
lvalue -= decrement
lvalue *= coefficient
lvalue /= divisor
lvalue %= modulus
lvalue ^= power
lvalue **= power
Maximális hordozhatóság érdekében ne használd a `**=' operátort.
A növelő és a csökkentő operátorok eggyel növelik vagy
csökkentik a változó értékét. Ugyanezt megteheted értékadó operátorral is,
így ez a két új operátor nem ad semmi újat az awk nyelvhez, de
kényelmes rövidítése egy gyakran használt műveletnek.
A növelő operátor a `++', használható mielőtt vagy miután a kifejezés értékét megkaptuk.
Ha a `++v'-t használjuk, akkor egyet ad a változóhoz és ez lesz a kifejezés értéke is. Ez teljesen azonos a `v += 1' kifejezéssel.
Ha a `++'-t a változó után írjuk, akkor bár ez is eggyel növeli a
változó értékét, de az a különbség, hogy a kifejezés értéke a változó
régi értéke lesz. Így, ha a foo értéke négy, akkor a
`foo++' kifejezés értéke is négy, de a `foo' változó ötre
változik.
A `foo++' majdnem azonos a `(foo += 1) - 1' kifejezéssel. Nem
egészen azonos, mivel az awk-ban minden szám lebegőpontos (valós):
így `foo + 1 - 1' nem biztos, hogy tökéletesen megegyezik `foo'-val.
Ez a különbség nem vehető észre ha "kis" számokat használsz
(kisebb mint 10e12).
Bármilyen `lvalue' növelhető. Mezőket és tömb elemeit pontosan úgy növel mint változókat. (Ha egy mezőre akarsz hivatkozni és ugyanakkor a változó értékét növelni a `$(i++)' kifejezést használd. A zárójelek fontosak a `$' precedenciája miatt.)
A csökkentő operátor `--' ugyanúgy viselkedik mint a növelő, csak kivon egyet a változóból. Mint a `++', használható az `lvalue' előtt vagy után.
Az alábbiakban összefoglaljuk a növelő és csökkentő operátorok használatát.
++lvalue
lvalue++
--lvalue
lvalue--
awk-ban
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.).
Az útikönyv pontos. A valóság gyakran nem egzakt. Galaxis útikönyv stopposoknak
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.
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.
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ábbi szimmetrikus táblázat 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 táblázata:
x < y
x <= y
x > y
x >= y
x == y
x != y
x ~ y
x !~ y
subscript in array
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.
(see section Szövegek és számok konverziója).
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:
1.5 <= 2.0
"abc" >= "xyz"
1.5 != " +2"
"1e2" < "3"
a = 2; b = "2"
a == b
a = 2; b = " +2"
a == b
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 kifejezésként
(see section Hogyan használjuk a reguláris kifejezéseket; és
see section Dinamikus reguláris kifejezések használata).
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:
$0 ~ /regexp/
Egy speciális eset, amikor a /foo/ nem a `$0 ~ /foo/'
kifejezés rövidítése, ha a reguláris kifejezés a `~' vagy a `!~'
operátor jobb oldalán áll!
See section Reguláris kifejezés konstansok használata,
ahol ezt az esetet részletesen tárgyaltuk.
A logikai kifejezések összehasonlító vagy illesztő kifejezések kombinációja a "vagy" (`||'), az "és" (`&&') és a "negálás" (`!') operátorokkal illetve a zárójelek segítségével. A logikai kifejezések igazság értéke a komponens kifejezések igazság értékének összekombinálásával kapható meg. A logikai kifejezéseket boolean kifejezésnek is hívják. A két név teljesen egyenértékű.
Logikai kifejezés használható minden olyan helyen, ahol összehasonlító
és illesztő kifejezés állhat. Szerepelhetnek az if, a while,
a do és a for kifejezésekben
(see section Vezérlésátadó kifejezések a tevékenységekben).
A logikai kifejzés értéke szám (egy ha igaz és zérus ha hamis), ami akkor
játszik fontos szerepet ha a logikai kifejezés eredményét egy változóban
tároljuk vagy aritmetikai kifejezésben használjuk.
Ráadásul minden logikai kifejezés egy érvényes minta is, így használható mint minta és így képes a szabályok végrehajtását is befolyásolni.
A három logikai operátor leírását néhány példával alább közöljük:
boolean1 && boolean2
if ($0 ~ /2400/ && $0 ~ /foo/) printA második kifejezés (boolean2) csak akkor értékelődik ki, ha a boolean1 igaz. Ez akkor lehet fontos, ha a boolean2 kiértékelésének van mellékhatása: a `$0 ~ /foo/ && ($2 == bar++)' kifejezés esetén a
bar változó nem növelődik meg ha a foo
szöveg nem szerepel a rekordban.
boolean1 || boolean2
if ($0 ~ /2400/ || $0 ~ /foo/) printA második kifejezés (boolean2) csak akkor értékelődik ki, ha a boolean1 hamis. Ez akkor lesz fontos ha a boolean2 kifejezés egy olyan kifejezést tartalmaz aminek mellékhatása van.
! boolean
awk '{ if (! ($0 ~ /foo/)) print }' BBS-list
A `&&' és a `||' operátorokat rövidre zárható operátoroknak is nevezik. A teljes kifejezés kiértékelése "rövidre záródik", befejeződik, ha az eredmény a kifejezés egy részének kiértékelésével már meghatározható.
A logikai kifejezések sorokra bonthatók a `&&' és a `||' operátorok
után beszúrt új sor karakterrel. Ugyanakkor az új sor karakter nem használható
az operátorok előtt a `\' karakter nélkül
(see section awk kifejezések és sorok).
A `!' operátort tartalmazó kifejezések értéke vagy egy vagy zérus lehet, attól függően, hogy milyen igazságértékű kifejezésre lett alkalmazva az operátor.
A `!' operátor gyakran használható kapcsoló változók átállítására igazról hamisra és fordítva. Például, az alábbi program az egyik módja annak, hogy speciális zárójelek közti sorokat kinyomtassunk:
$1 == "START" { interested = ! interested }
interested == 1 { print }
$1 == "END" { interested = ! interested }
Az interested változó, mint minden awk változó, kezdetben
zérus értékű, ami hamis értékű. Amikor egy `START' szöveggel kezdődő
sort olvas be, akkor az interested változót átállítja igazra a
`!' operátorral. A következő szabály addig nyomtatja ki a sorokat
amíg a interested változó igaz. Amikor egy `END' kezdetű
sort olvas be, a interested változót visszaállítja hamisra.
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.
See section Tömbök az awk-ban,
további információ a tömbökről.
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 használjuk
(see section awk kifejezések és sorok).
Ha a `--posix' opció meg van adva
(see section Command Line Options), akkor ez a kiegészítés nem
használható.
A függvény egy nevet rendel egy adott számításhoz. Mivel névvel
rendelkezik, ezért bármikor meghívható a programból. Például az
sqrt függvény egy szám négyzetgyökét számítja ki.
Egy adott számú beépített függvény áll rendelkezésre minden
awk programban. Az sqrt függvény ezek egyike.
See section Beépített függvények, ami
a beépített függvények listáját és
leírását tartalmazza.
Ráadásul saját függvényeket is lehet definiálni.
See section Felhasználó által definiált függvények,
hogy hogyan kell definiálni új függvényeket.
A függvényeket függvényhíváson keresztül lehet használni, ami a függvény nevét és a közvetlenül utána álló argument listát tartalmazza zárójelekben. Az argumentumok olyan kifejezések, amelyek extra adatot biztosítanak a függvénybeli számításhoz. Ha egynél több argumentum van, akkor az argumentumokat vesszővel kell elválasztani. Ha a függvénynek nincs argumentuma, akkor egy üres zárójelet kell írni, `()'. Íme néhány példa:
sqrt(x^2 + y^2) egy argumentum atan2(y, x) két argumentum rand() nincs argumentum
Nem szabad szóköz karaktert tenni a függvény neve és a nyitó zárójel közé! A felhasználó által definiált függvény nevek pont úgy néznek ki mint a változók nevei és ezért a szóközzel úgy lenne értelmezve mintha egy változót összefűznénk a zárójelek közti kifejezéssel. Beépített függvények esetén egy szóköz teljesen ártalmatlan a zárójel előtt, de a legjobb ha nem szoksz hozzá, mert így elkerülheted ezt a hibát az általad definiált függvények esetén.
Minden függvény egy adott számú argumentumot kíván. Például az sqrt
függvényt csak egyetlen argumentummal kell meghívni, azt a számot kell
megadni, aminek a négyzetgyökét ki akarjuk számolni:
sqrt(argument)
Néhány beépített függvény esetén az utolsó argumentum elhagyható. Ha az utolsó argumentumot nem adod meg, akkor egy ésszerű alapbeállítást használ a program. See section Beépített függvények, a teljes részletekről. Ha egy a felhasználó által definiált függvény argumentumaiból elhagyunk néhányat, akkor azok az argumentumok, mint lokális változók lesznek kezelve és üres szöveg lesz a kezdő értékük (see section Felhasználó által definiált függvények).
Mint minden más kifejezésnek, a függvényhívásnak is van értéke, amit a függvény számol ki az argumentumok alapján. Ebben a példában, a `sqrt(argument)' kifejezés kiszámolja az argument négyzetgyökét. A függvénynek is lehet mellékhatása, mint például bizonyos változókhoz értéket rendelni vagy I/O végrehajtása.
Itt egy parancs számok beolvasására, egy szám soronként majd kinyomtatja mindegyik beolvasott szám négyzetgyökét:
$ awk '{ print "The square root of", $1, "is", sqrt($1) }'
1
-| The square root of 1 is 1
3
-| The square root of 3 is 1.73205
5
-| The square root of 5 is 2.23607
Control-d
Az operátorok precedenciája azt határozza meg, hogy az operátorokat
hogyan lehet csoportosítani egy kifejezésben. Például a `*' operátornak
magasabb a precedenciája mint a `+' operátornak; így, a `a + b * c'
kifejezés esetén a b és a c változókat összeszorozza, majd
azután hozzáadja az a változót (például: `a + (b * c)').
Az operátorok precedenciáját felül lehet bírálni a zárójelek használatával. Úgy is gondolhatsz a precedenciaszabályokra, mint amelyek meghatározzák azt, hogy a zárójeleknek hol kellene lenniük, ha nem írod ki őket. Valójában hasznos a zárójeleket mindig használni, ha az operátorok valamilyen szokatlan kombinációját használjuk, mivel más emberek, akik a programodat olvassák, később lehet hogy nem emlékeznének, hogy mi is a helyes precedencia az adott esetben. Lehet, hogy Te is elfelejted; így te is hibázhatsz. A zárójelek használatával az ilyen hibák elkerülhetők.
Amikor azonos precedenciájú operátorokat használunk, a legbaloldalibb operátor alapján lesz először csoportosítva a kifejezés, kivéve persze az értékadás, a feltételes és az exponenciális operátorok, amelyeknél a sorrend fordított. Így a `a - b + c' csoportosítása `(a - b) + c' és a `a = b = c' csoportosítása `a = (b = c)'.
Az unáris operátorok esetén a precedencia nem számít egészen addig amig csak unáris operátorokat használunk, mivel csak egyféleképpen lehet értelmezni őket -- a legbelsőt először. Így a `$++i' azt jelenti, hogy `$(++i)' és a `++$x' jelentése `++($x)'. Ugyanakkor, amikor egy másik operátor áll az operandus után, akkor az unáris operátor precedenciája is fontos. Így `$x^2' jelentése `($x)^2', de `-x^2' azt jelenti, hogy `-(x^2)', mivel a `-' operátornak alacsonyabb precedenciája van mint a `^' operátornak, míg a `$' operátornak magasabb a precedenciája.
Az alábbi táblázat tartalmazza az awk operátorok listáját a legmagasabb
precedenciától az alacsonyabb felé:
(...)
$
++ --
^ **
+ - !
* / %
+ -
Összefűzés
< <= == !=
> >= >> |
~ !~
in
&&
||
?:
= += -= *=
/= %= ^= **=
Mint azt már láttuk, minden awk szabály tartalmaz egy mintát és egy ahhoz
kapcsolódó tevékenységet. Ez a fejezet azt mutatja be, hogy hogyan kell
mintákat és tevékenységeket összeállítani.
Az awk-ban a minták határozzák meg a szabályok végrehajtási sorrendjét:
a szabály végrehajtódik, ha a minta illeszkedik az adott bemeneti rekordra.
Ez a bekezdés elmagyarázza, hogy hogyan írjunk mintákat.
Az alábbi táblázat felsorolja az awk-ban használható minták típusát.
/reguláris kifejezés/
kifejezés
pat1, pat2
BEGIN
END
awk programokban.
(See section A BEGIN és az END speciális minták.)
üres
Már eddig is használtunk reguláris kifejezéseket mintaként a példákban. Ez a típusú minta egy egyszerű regexp konstans a szabály minta pozíciójában, és jelentése megfelel a `$0 ~ /pattern/' kifejezésnek. A minta illeszkedik, amikor a bemeneti rekordra illeszkedik a regexp. Például:
/foo|bar|baz/ { buzzwords++ }
END { print buzzwords, "buzzwords seen" }
Bármilyen awk kifejezés érvényes awk minta és a minta illeszkedik
ha a kifejezés értéke nem zérus (ha egy szám) vagy nem üres szöveg.
A kifejezések minden alkalommal kiértékelődnek, amikor a szabályt teszteli
egy új bemeneti rekorddal. Ha a kifejezés mezőket használ mint $1,
az érték közvetlenül függ az új bemeneti rekord szövegétől; máskülönben
attól függ, hogy mi történt eddig az awk program végrehajtása során.
Egy nagyon gyakori kifejezés mint minta az összehasonlító kifejezés, ami összehasonlító operátort használ, see section Változó típusok és az összehasonlító kifejezések.
A regexp illeszkedés és nem illeszkedés is gyakori kifejezések. A `~'
és a `!~' operátorok bal oldali operandusa egy szöveg. A jobb
oldali operandus vagy egy konstans reguláris kifejezés a `/' karakterek
közé zárva (/regexp/) vagy bármilyen kifejezés aminek a szöveg
értéke mint dinamikus reguláris kifejezés használható
(see section Dinamikus reguláris kifejezések használata).
A következő példa minden olyan bemeneti rekord második rekordját kinyomtatja aminek az első mezője pontosan a `foo' szöveg.
$ awk '$1 == "foo" { print $2 }' BBS-list
(Nincs kimenet, mivel nincs "foo" nevű BBS állomás.) Ezzel ellentétben nézzük az alábbi reguláris kifejezést, ami bármilyen rekordra illeszkedik aminek az első mezője `foo' szöveget tartalmaz.
$ awk '$1 ~ /foo/ { print $2 }' BBS-list
-| 555-1234
-| 555-6699
-| 555-6480
-| 555-2127
A logikai kifejezések szintén gyakran használt minták, és hogy egy minta illeszkedik egy bemeneti rekordra attól függ, hogy az alkifejezés illeszkedik-e.
Például az alábbi parancs kinyomtat minden olyan rekordot a `BBS-list' file-ból, ami tartalmazza a `2400' és a `foo' szövegeket.
$ awk '/2400/ && /foo/' BBS-list -| fooey 555-1234 2400/1200/300 B
Az alábbi parancs kinyomtat minden olyan rekordot a `BBS-list' file-ból, ami vagy a `2400' vagy a `foo' vagy mindkettő szöveget tartalmazza.
$ awk '/2400/ || /foo/' BBS-list -| alpo-net 555-3412 2400/1200/300 A -| bites 555-1675 2400/1200/300 A -| fooey 555-1234 2400/1200/300 B -| foot 555-6699 1200/300 B -| macfoo 555-6480 1200/300 A -| sdace 555-3430 2400/1200/300 A -| sabafoo 555-2127 1200/300 C
Az alábbi parancs kinyomtat minden olyan rekordot a `BBS-list' file-ból, ami nem tartalmazza a `foo' szöveget.
$ awk '! /foo/' BBS-list -| aardvark 555-5553 1200/300 B -| alpo-net 555-3412 2400/1200/300 A -| barfly 555-7685 1200/300 A -| bites 555-1675 2400/1200/300 A -| camelot 555-0542 300 C -| core 555-2912 1200/300 C -| sdace 555-3430 2400/1200/300 A
Egy mintában előforduló logikai operátor alkifejezései lehetnek konstans
reguláris kifejezések, összehasonlító vagy bármilyen awk kifejezések.
A tartomány minták nem kifejezések, így nem szerepelhetnek logikai
kifejezésekben. A BEGIN és az END speciális minták soha nem
illeszkednek bemeneti rekordra, nem kifejezések és nem szerepelhetnek logikai
mintában.
Egy regexp konstans mint minta szintén egy speciális kifejezés minta. A
/foo/ mint kifejezésnek egy az értéke ha a `foo' szöveg előfordul
a jelenlegi bemeneti rekordban; így, mint minta, a /foo/ illeszkedik
minden olyan rekordra ami tartalmazza a `foo' szöveget.
A tartomány minta két vesszővel elválasztott mintából áll az alábbi formában `kezdminta, végminta', és a bemeneti rekordok egymás utáni tartományára illeszkedik. Az első minta, kezdminta, adja meg a tartomány kezdetét és a második, végminta, határozza meg a végét. Például:
awk '$1 == "on", $1 == "off"'
kinyomtat minden rekordot az `on'/`off' pár között, a kezdő és a záró sorokat is beleértve.
Tartomány minta esetén először a kezdminta mintát keresi meg minden bemeneti rekord között. Amikor sikerül megtalálni a kezdminta mintát, a tartomány minta bekapcsol. A tartomány minta illeszkedik az adott rekordra is, és addig amíg bekapcsolva marad, automatikusan illeszkedik minden beolvasott bemeneti rekordra. Ezen kívül minden rekordot megpróbál illeszteni az végminta mintával is; amikor ez sikerül a tartomány minta kikapcsol a következő rekordra. Ekkor ismét a kezdminta mintát keresi a rekordok között.
Az a rekord, ami be- illetve kikapcsolja a tartomány mintát szintén illeszkedik
a tartomány mintára. Ha ezeken a rekordokon nem akarunk dolgozni, akkor egy
if kifejezést kell használni a szabályok tevékenység részében, hogy
kigyüjtsük azokat a rekordokat amik érdekelnek.
Lehetséges, hogy egy minta be- és ki is kapcsolódik ugyanazon rekord által, ha a rekord mindkét feltételt teljesíti. Ekkor a tevékenység csak az adott rekordra hajtódik végre.
Például, tegyük fel hogy két azonos jel (mondjuk a `%' szimbólum)
közötti szöveged van, amit szeretnél
elhagyni egy másik szövegből. Összekombinálhatod a tartomány mintát a next
kifejezéssel
(eddig nem tárgyaltuk, see section A next kifejezés),
aminek hatására az awk átugorja a jelenlegi rekord minden további
feldolgozását, és újrakezdi a következő rekorddal. Egy ilyen program így
nézne ki:
/^%$/,/^%$/ { next }
{ print }
Ez a program nem működik, mivel a tartomány mintát be- és ki is kapcsolja az első sor ami a `%' szimbólumot tartalmazza. Ahhoz, hogy valóban működjön a program, valahogy így kellene kinéznie:
/^%$/ { skip = ! skip; next }
skip == 1 { next } # skip lines with `skip' set
Érdemes megjegyezni, hogy a tartomány mintában a vesszőnek (`,') van a legalacsonyabb precedenciája és ezért utolsóként értékelődik ki az operátorok közül. Így, például, a következő program megpróbál egy tartomány mintát és egy egyszerűbb tesztet összekombinálni.
echo Yes | awk '/1/,/2/ || /Yes/'
Ennek a programnak a szerzője úgy értette, hogy `(/1/,/2/) || /Yes/', de
az awk ezt úgy értelmezi, hogy `/1/, (/2/ || /Yes/)'. Ezt
nem lehet megváltoztatni vagy valami trükkel elkerülni; tartomány minták
nem kombinálhatók más mintákkal.
BEGIN és az END speciális minták
A BEGIN és az END speciális minták. Ezek a minták nem
illeszkednek semmilyen bemeneti rekordra, ezzel szemben kezdő és
lezáró tevékenységet biztosítanak egy awk program számára.
A BEGIN szabály csak egyszer hajtódik végre, mielőtt beolvasná az
első bemeneti rekordot. Az END szabály is csak egyszer hajtódik
végre, miután beolvasott minden bemenetet. Például:
$ awk '
> BEGIN { print "Analysis of \"foo\"" }
> /foo/ { ++n }
> END { print "\"foo\" appears " n " times." }' BBS-list
-| Analysis of "foo"
-| "foo" appears 4 times.
Ez a program megszámolja azokat a rekordokat a `BBS-list' file-ban,
amelyek tartalmazzák a `foo' szöveget. A BEGIN szabály
kinyomtatja a report fejlécét, ugyanakkor nincs arra szükség, hogy a
BEGIN szabályban az n számlálót zérus értékkel inicializáljuk,
mivel az awk megteszi ezt automatikusan (see section Változók).
A második szabály megnöveli az n változót minden alkalommal, amikor
a rekord tartalmazza a `foo' mintát. Az END szabály kinyomtatja
az n változó értékét a futás végén.
A BEGIN és az END speciális szabályokat nem lehet sem
tartomány mintában, sem logikai operátorral
használni (valójában semmilyen operátorral nem lehet kombinálni).
Egy awk program tartalmazhat több BEGIN és/vagy END
szabályt is. A megjelenési sorrendben hajtódnak végre, a BEGIN
szabályok kezdetben és az END szabályok a program legvégén.
A BEGIN és az END szabályok összekeverhetők más
szabályokkal. Ezt a lehetőséget 1987-ben adták az awk-hoz és
bekerült a POSIX szabványba. Az awk eredeti (1987-es) verziója
megkívánta, hogy a BEGIN szabály a program elején álljon és az
END szabály a legvégén, ezenkívül csak egy BEGIN és csak
egy END minta volt használható. Ez ma már nincs így, de jó ötlet
ha a program olvashatóságát vagy szervezését tekintjük fő szempontnak.
A többszörös BEGIN és END szabályok hasznosak könyvtár
függvények írásánál, mivel minden könyvtári file-nak lehet saját
BEGIN és/vagy END szabálya, ami elvégzi a szükséges
inicializálást és/vagy takarítást. Fontos figyelembe venni, hogy amilyen
sorrendben a könyvtári függvények megjelennek a parancssorban az meghatározza
a BEGIN és az END szabályok végrehajtási sorrendjét is. Ezért
fontos, hogy óvatosan írjunk ilyen szabályokat a könyvtár file-okba, mivel
így a végrehajtási sorrend nem számít.
See section A Library of awk Functions,
ami bemutat néhány hasznos könyvtári függvényt.
Ha egy awk programban csak egy BEGIN szabály van és semmilyen
más szabály nincs akkor a program kilép a BEGIN szabály végrehajtása
után. (Az awk eredeti verizója folyamatosan olvasott, és minden bemenetet
eldobott addig, amíg egy file vége jelet nem kapott.) Ugyanakkor ha van egy
END szabály a programban, akkor a bemenetet mindenképpen olvasni
fogja, még akkor is, ha nincs semmilyen más szabály a programban. Ez szükséges
abban az esetben ha az END szabály használná a FNR és a
NR változókat (s.s.).
A BEGIN és az END szabályoknak kell legyen tevékenység része;
ezeknél a szabályoknál nincs alaptevékenység.
BEGIN és az END szabályokban
Van néhány (néha csak apró), de fontos kérdés az I/O-val kapcsolatban,
amikor a BEGIN vagy az END szabályokon belül használjuk.
Az első kérdés, hogy mi lesz a $0 értéke a BEGIN szabályon
belül. Mivel a BEGIN szabály a bemenet beolvasása előtt hajtódik végre,
ezért nincs semmilyen bemeneti rekord, és nincsennek mezők a BEGIN
szabály végrehajtása során. Ha a $0-ra vagy bármilyen mezőre hivatkozunk,
akkor egy üres szöveget vagy zérus értéket kapunk, az adott helyzettől
függően. Az egyik lehetőség, hogy a $0-nak valódi értéket adjunk a
getline parancs használatával
(see section Explicit beolvasás getline-al).
A másik lehetőség, hogy értéket rendelünk hozzá.
A második kérdés hasonló az elsőhöz, de a másik irányból közelíti meg
a problémát. Az END szabályon belül mi a $0 és az NF
értéke? Hagyományosan, főleg az implementációknak köszönhetően, a $0
és a NF értéke nem definiált az END szabályon belül.
A POSIX szabvány azt definiálja, hogy az NF elérhető az END
szabályon belül, és az utolsó bemeneti rekordban előforduló mezők számát
tartalmazza. Valószínűleg csak tévedésből a szabvány nem rendelkezik a
$0 értékének megőrzéséről, de ez lenne logikus. Valójában a
gawk így is tesz és megőrzi a $0 értékét az END
szabályon belül, de jegyezd meg, hogy a UNIX awk és valószínűleg más
implementációk nem így viselkednek.
A harmadik kérdés következik az első kettőből. Mit jelent a `print'
parancs a BEGIN és az END szabályon belül? A jelentése
persze ugyanaz, `print $0'. Ha a $0 egy üres szöveg, akkor egy
üres sort nyomtat ki. Régen az awk programozók a BEGIN és az
END szabályokon belül a `print' parancsot használták a
`print ""' helyett, abban bízva, hogy a $0 egy üres szöveg.
Bár ez gyakran igaz a BEGIN szabályon belül, legalább is a gawk-ban,
de nagyon rossz ötlet az END szabályon belül. Ugyanakkor rossz
programozói stílus is, mivel ha üres sort akarunk kinyomtatni, akkor adjuk
azt meg a programnak.
Az üres (vagyis nem létező) minta illeszkedik minden bemeneti rekordra. Például, a program:
awk '{ print $1 }' BBS-list
kinyomtatja minden rekord első mezőjét.
Egy awk program "script"-szabályok és függvénydefiníciók
keveréke. (A függvényeket később tárgyaljuk,
see section Felhasználó által definiált függvények.)
Egy szabály egy mintát és egy tevékenységet tartalmaz, bármelyik (de egyszerre
mind a kettő nem) hagyható el. A tevékenység mondja meg az awk-nak,
hogy mit kell csinálni, ha megtalálta a keresett mintát. Úgy nagyjából,
egy awk program így néz ki:
[minta] [{ tevékenység }]
[minta] [{ tevékenység }]
...
function név(argumentumok) { ... }
...
Egy tevékenység egy vagy több awk kifejezésből áll kapcsos
zárójelek között (`{' és `}'). Minden kifejezés meghatároz
egy dolgot. A kifejezéseket új sor vagy pontosvessző karakterek választják el
egymástól.
A kapcsos zárójelekre mindig szükség van a tevékenységek körül még akkor is, ha csak egy kifejezésből áll, vagy nincs is benne kifejezés. Ugyanakkor ha a tevékenységet elhagyjuk, akkor a kapcsos zárójeleket is el lehet hagyni. Az elhagyott tevékenység megegyezik a `{ print $0 }' kifejezéssel.
/foo/ { } # foo-ra illeszkedik, nem csinál semmit
/foo/ # foo-ra illeszkedik, kinyomtatja a rekordot
Az awk-ban használható kifejezéseket alább soroljuk fel:
awk program futásának folyamatát
határozzák meg. Az awk nyelv a C programozási nyelvben használható
utasításokat biztosítja (if, for, while és do)
és néhány speciálisat
(see section Vezérlésátadó kifejezések a tevékenységekben).
if, a while, a do vagy a for
kifejezések testében.
getline parancs
(see section Explicit beolvasás getline-al), a next
parancs
(see section A next kifejezés),
és a nextfile parancs
(see section A nextfile kifejezés).
print és printf.
See section Kimenet megjelenítése.
delete kifejezés.
A következő fejezet a vezérlésátadó kifejezéseket tárgyalja részletesen.
A vezérlésátadó kifejezések, mint az if, a while és így tovább,
az awk program végrehajtásának folyamatát befolyásolják. Az awk
vezérlésátadó kifejezései a C programozási nyelv kifejezésein alapulnak.
Minden vezérlésátadó kifejezés egy speciális kulcsszóval kezdődik, mint az if
és a while, azért hogy megkülönböztethetők legyenek az egyszerű
kifejezésektől.
Sok vezérlésátadó kifejezés magába foglal más kifejezéseket is; például
az if kifejezés olyan kifejezéseket is tartalmaz, amiket vagy végrehajt
vagy nem. Ezek a kifejezések alkotják a vezérlésátadó kifejezés testét.
Ha egynél több kifejezést akarunk összefogni a vezérlésátadó kifejezés testében
akkor ezt egy összetett kifejezéssel, kapcsos zárójelek között lehet
megtenni. A kifejezéseket új sorral vagy a pontos vessző karakterrel lehet
elválasztani egymástól.
if-else kifejezés
Az if-else kifejezés az awk döntéshozó kifejezése és
így néz ki:
if (feltétel) then-test [else else-test]
A feltétel kifejezés befolyásolja, hogy a kifejezés további része
mit csinál. Ha a feltétel igaz, akkor a then-test hajtódik
végre; egyébként a else-test fut le.
A kifejezés else része opcionális. A feltétel hamis ha az értéke
zérus vagy üres szöveg, egyébként igaz.
Íme egy példa:
if (x % 2 == 0)
print "x páros"
else
print "x páratlan"
Ebben a példában, ha a `x % 2 == 0' kifejezés igaz (vagyis az x
értéke osztható kettővel), akkor az első print parancs hajtódik végre,
más esetben a második print fog lefutni.
Ha az else a then-testel egy sorban jelenik meg és a
then-test nem összetett kifejezés (pl. nincs kapcsos zárójelek között),
akkor egy pontos vesszőnek kell elválasztania a then-testet az
else-től. Ennek bemutatására, írjuk át az előző példát:
if (x % 2 == 0) print "x páros"; else
print "x páratlan"
Ha elfelejtjük a `;' -t kitenni, akkor az awk nem képes
értelmezni a kifejezést és szintaktikai hibát fog jelezni.
Valójában nem érdemes így írni a példát, mivel egy emberi olvasó lehet,
hogy nem veszi észre az else kifejezést, ha az nem az első szó
a sorban.
while kifejezésA programozásban a hurok vagy a ciklus azt jelenti, hogy a program egymás után kétszer vagy többször hajtja végre ugyanazt a részt.
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.
do-while kifejezés
A do hurok a while hurok kifejezés egy változata. A do
hurok kifejezés egyszer végrehajtja a testet, és ezt addig ismétli
amíg a feltétel igaz. A formája az alábbi:
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.
for kifejezés
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
(see section 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]
A section Egy tömb elemeinek ellenőrzése,
további információt tartalmaz a for ciklus ezen verziójáról.
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.
A következő fejezetben bonyolultabb for ciklusokat mutatunk be.
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. (Ez teljesen különböző
az exit kifejezéstől, ami az egész awk programból lép ki.
See section Az exit kifejezés.)
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
(see section A 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
(see section Command Line Options).
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.).
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
(see section A next kifejezés).
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
(see section Command Line Options).
Minden más esetben ez hibát jelent, mivel a POSIX szabvány a continue
ilyen viselkedését nem definiálja (s.s.).
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
(see section Explicit beolvasás getline-al). 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.
See section Speciális file nevek gawk-ban.
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
(see section Felhasználó által definiált függvények).
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
(see section Felhasználó által definiált függvények).
A gawk-nak nincs ilyen problémája.
nextfile kifejezés
A gawk a next kifejezéshez hasonlóan egy nextfile
kifejezést is biztosít. Ugyanakkor az aktuális rekord eldobása helyett
a nextfile kifejezés arra utasítja a gawk-ot, hogy
fejezze be az adott adat file feldolgozását.
A nextfile kifejezés hatására a parancssorbeli következő
file nevével tölti fel a FILENAME változót, az FNR változót
egyre állítja, az ARGIND változót megnöveli és elkezdi a
feldolgozást a program első szabályától.
See section Beépített változók.
Ha a nextfile kifejezés hatására eléri a bemenet végét, akkor az
END szabály kódja hajtódik végre.
See section A BEGIN és az END speciális minták.
A nextfile kifejezés egy gawk kiegészítés; (jelenleg)
nem használható más awk implementációkban.
See section Implementing nextfile as a Function,
ahol bemutatjuk, hogy a felhasználó által definiált függvénnyel
is szimulálható a nextfile kifejezés.
A nextfile kifejezés hasznos lehet ha több adatfile-t kell feldolgozni,
és nem akarod minden file minden rekordját feldolgozni. Normális esetben
ahhoz, hogy a következő file feldogozását elkezdhesd, a szükségtelen
rekordokat is át kellene nézni. A nextfile kifejezés ezt kerüli el
hatékonyan.
Figyelem: A gawk 3.0-ás verziója előtt két azonos szó
állt rendelkezésre ugyanarra a kifejezésre, a `next file' és a
nextfile. Ez megváltozott a 3.0-ás verziótól kezdve, mivel a
file kulcsszó kezelése így nem volt következetes. Ha a next
után szerepelt akkor kulcsszó volt, egyébként meg egy egyszerű azonosító.
A régi használat még mindig elfogadott. Ugyanakkor a gawk figyelmeztető
üzenetet generál és a gawk jővőbeli verzióiban a next file
kifejezés nem támogatott.
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:
exit [visszatérési érték]
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
(see section A BEGIN és az END speciális minták).
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.
See section Assertions,
ami egy példát tartalmaz erre.
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")
}
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.
awk-ot 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.
CONVFMT
sprintf függvény első argumentuma lesz
(see section Szövegmanipuláló beépített függvények).
Az alapértéke "%.6g".
A CONVFMT változót a POSIX szabvány vezette be.
FIELDWIDTHS *
gawk-nak, hogy
a bemenetet hogyan kell feldarabolni fix szélességű oszlopokra. Ez csak egy
kísérleti megoldás. Ha a FIELDWIDTHS-nek értéket adunk akkor az
FS nem érvényes többé a mezők darabolásánál.
See section Meghatározott szélességű adatok beolvasása, további információért.
Ha a gawk "compatibility" módban van
(see section Command Line Options), akkor a FIELDWIDTHS
változónak nincs speciális jelentése, és a mezőkre darabolás kizárólag
az FS változó alapján történik.
FS
FS változó adja meg a bemeneti mezőelválasztót
(see section Hogyan történik a mezőelválasztás).
A változó értéke egy egy-karakteres szöveg vagy több karakterből álló
reguláris kifejezés ami illeszkedik a mezőket elválasztó szövegre. Ha az
értéke egy üres szöveg ("") akkor a rekordon belül minden karakter egy
különálló mező lesz.
A változó alapértéke a " ", egy szóközből álló szöveg. Ennek a
speciális jelentésa az, hogy szóközök, tab és/vagy új sor karakterek
sorozata alkotja a mezőelválasztót.(9) Egy másik speciális tulajdonság, hogy ebben
az esetben a kezdő és végső szóközök, tab és új sor karaktereket nem veszi
figyelembe.
Az FS változó értékét a parancssorból is be lehet állítani a `-F'
opcióval:
awk -F, 'program' input-filesHa a
gawk a FIELDWIDTHS változót használja a mezők
feldarabolására, akkor ha egy értéket rendelünk az FS változóhoz a
gawk visszakapcsol az alap elválasztó módba. Ennek egy egyszerű
módja az `FS = FS' kifejezés.
IGNORECASE *
IGNORECASE nem zérus vagy nem üres szöveg, akkor minden szöveg
összehasonlításnál és reguláris kifejezés illesztésnél nem tesz különbséget a
kis- és nagybetűk között. Így a reguláris kifejezések illesztése a `~'
és a `!~' operátorokkal és a gensub
a gsub, az index, a match, a split és a sub
függvények, az RS rekordelválasztó és az FS mezőelválasztó nem
tesz különbséget a kis- és a nagybetűk között. Az IGNORECASE változó
értéke nem befolyásolja a tömbök elemeire való hivatkozásnál használt
szövegeket.
See section Kis- és nagybetűk az illesztésekben.
Ha a gawk "compatibility" módban van
(see section Command Line Options),
akkor az IGNORECASE változónak nincs speciális jelentése és a szöveg és
a regexp műveletekben megkülönbözteti a kis- és nagybetűket.
OFMT
print kifejezés használ. Lényegében mintha a sprintf
függvénynek az első argumentumát adnánk meg
(see section Szövegmanipuláló beépített függvények).
Alapesetben az értéke: "%.6g". Az awk korábbi verzióiban
minden általános kifejezés esetén amikor egy számot szöveggé kellett konvertálni
az awk a OFMT változót használta; ma már a CONVFMT
változót használja erre.
OFS
print kifejezés mezőket nyomtat ki, akkor a változó értékét
nyomtatja ki a mezők közé. Az alapérték " ", egy egyetlen szóközből
álló szöveg.
ORS
print kifejezés által kinyomtatott
rekordok között nyomtatja ki a változó értékét. Az alapértéke: "\n".
(See section Kimeneti elválasztó.)
RS
awk bemeneti rekordelválasztója. Az alapértéke egy olyan
szöveg, ami csak egy új sor karaktert tartalmaz. Ez azt jelenti, hogy a bemeneti
rekord egyetlen sorból áll. Az értéke lehet üres szöveg is, így a rekordokat
vagy üres sorok vagy regexp kifejezések illeszkedései választják el egymástól.
(See section Hogyan történik a feldarabolás rekordokra.)
SUBSEP
SUBSEP változó értéke választja el egy több dimenziós tömb indexeit
és az alapértéke "\034". Így ez a
kifejezés foo["A", "B"] valójában foo["A\034B"] kifejezéssel
egyezik meg
(see section Többdimenziós tömbö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.
ARGC
ARGV
awk program számára elérhető parancssori argumentumokat az
ARGV tömb tárolja. Az ARGC változó a parancssori argumentumok
számát tárolja.
See section Other Command Line Arguments.
Más awk tömböktől eltérően az ARGV elemeinek az indexe
zérustól (ARGC - 1) -ig terjed. Például:
$ 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] tartalma a
"BBS-list" szöveg. Az ARGC értéke három, eggyel több mint
az ARGV tömb utolsó elemének indexe, mivel a tömb elemeit zérustól
számozza.
Az ARGC és az ARGV nevek, a zérustól (ARGC - 1)-ig
indexelés a C programozási nyelvből származnak
See section Az ARGC és az ARGV változók használata, ami további
információt ad arról, hogy az awk hogyan használja ezeket a változókat.
ARGIND *
ARGV
tömbben. Minden alkalommal, amikor a gawk megnyit egy file-t
feldolgozásra, beállítja az ARGIND változót az ARGV tömb
elemének indexére. Amikor a gawk egy bemeneti file-t dolgoz fel a
`FILENAME == ARGV[ARGIND]' kifejezés mindig igaz.
Ez a változó a file-ok feldolgozásában hasznos, mivel ez a változó mondja
meg, hogy hol tartunk a file-ok feldolgozásában, és így meg lehet
különböztetni a parancssorban megadott azonos file neveket.
Bár az ARGIND változó beállítható az awk programból, a
gawk ezt a változót automatikusan beállítja az új értékre amikor
elkezd feldolgozni egy új file-t.
Ez a változó egy gawk kiegészítés. Más awk implementációkban
vagy ha a gawk "compatibility" módban van
(see section Command Line Options),
akkor a változónak nincs speciális jelentése.
ENVIRON
ENVIRON["HOME"]
értéke a /home/arnold lehet. Ha a tömb elemeit megváltoztatjuk,
az nem befolyásolja azt a környezetet, amit az awk átad egy
általa átirányítással vagy a system függvénnyel indított programnak.
(A gawk jövőbeli verzióiban ez lehet, hogy megváltozik.)
Néhány operációs rendszernek nincs környezeti változója. Ezeken a rendszereken
a ENVIRON változó üres (kivéve a ENVIRON["AWKPATH"] értékét).
ERRNO *
getline-nál alkalmazott átirányítás, vagy a getline
olvasás során, vagy egy close művelet alatt rendszer hiba lép fel, akkor
az ERRNO változó egy a hibát leíró szöveget fog tartalmazni.
Ez a változó egy gawk kiegészítés. Más awk implementációkban
vagy ha a gawk "compatibility" módban van
(see section Command Line Options),
akkor a változónak nincs speciális jelentése.
FILENAME
awk által éppen feldolgozás alatt álló
file nevét. Amikor a parancssorban nincs file név megadva, és az awk
a szabványos bemenetről olvas be, a FILENAME értéke "-".
A FILENAME értéke megváltozik minden alkalommal, amikor egy új file-t
kezd el feldolgozni
(see section Bemeneti file-ok olvasása).
A BEGIN szabályon belül a FILENAME értéke "", mivel
ebben a szabályban még nem dolgoz fel file-okat.(10) (s.s.)
FNR
FNR
változó. Minden alkalommal, amikor az awk beolvas egy új rekordot
a változó értéke megnövelődik
(see section Explicit beolvasás getline-al).
Ha egy új file-t kezd el feldolgozni, akkor a változó értékét zérusra
állítja.
NF
NF változó az aktuális bemeneti rekordban található mezők
számát tartalmazza. Az NF változó értékét az awk mindig
beállítja, amikor egy új rekordot olvas be, vagy egy új mezőt hozunk létre,,
vagy a $0 megváltozik
(see section Mezők elérése).
NR
awk által a program futásának kezdetétől eddig feldolgozott
bemeneti rekordok számát tartalmazza
(see section Hogyan történik a feldarabolás rekordokra).
Minden rekord beolvasása során az awk beállítja az NR értékét.
RLENGTH
match függvény által illesztett részszöveg hosszát tartalmazza
az RLENGTH változó
(see section Szövegmanipuláló beépített függvények).
Az RLENGTH változót a match függvény hívása állítja be.
Az értéke az illeszkedő szöveg hossza vagy -1, ha nem volt illeszkedés.
RSTART
match függvény által illesztett részszöveg kezdeti pozícióját adja
meg az RSTART változó
(see section Szövegmanipuláló beépített függvények).
Az RSTART változót a match függvény hívása állítja be.
Az értéke az illeszkedő szöveg kezdeti pozíciója vagy zérus, ha nem volt
illeszkedés.
RT *
awk beállítja az RT változó
értékét. A változó értéke a rekordelválasztóra, RS, illeszkedő
bemeneti szöveget tartalmazza.
Ez egy gawk kiegészítés. Más awk implementációkban vagy ha a
gawk "compatibility" módban van
(see section Command Line Options),
a változónak nincs speciális jelentése.
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
(see section Major Changes between V7 and SVR3.1),
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.
ARGC és az ARGV változók használata
A section Információt hordozó beépített változók, alatt
ezt a programot láthattad, ami bemutatja, hogyan használható az ARGC
és az ARGV változókban tárolt információ:
$ 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
(see section Command Line Options).
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
(see section A delete kifejezés).
Tipikusan ezt a feldolgozást a BEGIN szabályon belül érdemes elvégezni
mielőtt a bemeneti file-ok feldolgozása megkezdődik.
See section Splitting a Large File Into Pieces, és lásd még
section Duplicating Output Into Multiple Files, 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.
awk-ban
Egy tömb az értékek, azaz elemek, táblája. A tömbök elemeit
az indexek különböztetik meg. Az indexek lehetnek számok vagy
szövegek. Mivel az awk egyetlen halmazt tart fenn a változók, tömbök
és függvények neveire
(see section Felhasználó által definiált függvények),
ezért nem használható ugyanaz a név változóként és tömbként ugyanabban az
awk programban.
Az awk nyelv egydimenziós tömböket biztosít összetartozó
szövegek és számok csoportban tárolására.
Minden awk tömbnek kell legyen neve. A tömbök nevének ugyanaz a
formája, mint a változók neveinek; bármilyen érvényes változónév egy
érvényes tömbnév lehet. De nem használhatod ugyanazt a nevet változónak
és tömbnek is ugyanabban az awk programban.
Az awk tömbök felületesen hasonlítanak más programozási nyelvek
tömbjeire; de alapvető különbségek vannak. Az awk-ban nem kell megadni
a tömb méretét mielőtt használnánk. Ráadásul bármilyen szöveg vagy szám
használható indexként, nem csak egymás utáni egész számok.
Más programozási nyelvekben egy tömböt definiálni kell, és meg kell adni, hogy hány elemet fog tartalmazni. Ezekben a nyelvekben, a definiálás egy folyamatos memóriatömböt foglal le az elemek számára. A tömb indexei pozitív, egész számok kell legyenek; például, az első elemre a zérus indexel lehet hivatkozni, ami a memóriablokk legelején helyezkedik el. A második elemre az egyes indexel lehet hivatkozni, ami közvetlenül az első mellett helyezkedik el, és így tovább. Lehetetlen több elemet adni a tömbhöz, mivel csak az adott méretű hely lett lefoglalva. (Néhány programozási nyelv megenged tetszőleges kezdő vagy vég indexet, pl. `15 .. 27', de a tömb mérete csak adott méretű lehet.)
Koncepcionálisan egy négy elemű tömb így néz ki, ha az elemek a 8, a
"foo", a "" és a 30:
Csak az elemek tárolódnak; az indexek implicit módon következnek az értékekből. A 8 a zérus indexű elem értéke, mivel a 8 a zérus pozíciójú elemben található.
Az awk tömbjei asszociatívak. Ez azt jelenti,
hogy a tömb index és érték párok gyűjteménye:
Elem 4 Érték 30 Elem 2 Érték "foo" Elem 1 Érték 8 Elem 3 Érték ""
A párokat kevert sorrendben közöltük mivel a sorrend nem számít.
Az asszociatív tömbök egy nagy előnye, hogy új elemeket bármikor hozzá lehet
adni. Például tegyük fel, hogy a fenti tömbhöz egy tizedik elemet kell
hozzáadni aminek az értéke "number ten". Az eredmény:
Elem 10 Érték "number ten" Elem 4 Érték 30 Elem 2 Érték "foo" Elem 1 Érték 8 Elem 3 Érték ""
most a tömb szórt, "sparse", ami azt jelenti, hogy bizonyos indexű elemek hiányoznak: a tömbnek megvannak az 1--4 -ig terjedő elemei és a 10. eleme, de nincs 5, 6, 7, 8 vagy 9 -es eleme.
Az asszociatív tömbök egy másik következménye, hogy az indexeknek nem kell pozitív egész számoknak lenniük. Bármilyen szám vagy szöveg indexként használható. Például itt közlünk egy tömböt, ami lefordítja az angol szavakat franciára:
Elem "dog" Érték "chien" Elem "cat" Érték "chat" Elem "one" Érték "un" Elem 1 Érték "un"
Itt az egynek nem csak a szöveg formája hanem a szám formája is szerepel a szótárban -- így illusztrálható, hogy egy tömbben szám és szöveg index is lehet. (Valójában a tömb indexe mindig szöveg; ezt részletesen tárgyaljuk a section Számok használata mint tömb index alatt.)
Az IGNORECASE értéke nincs hatással a tömb indexére. Pontosan
ugyanazt a szöveget kell használni a tömb eléréséhez, mint amit az érték
tárolásánál használtunk.
Amikor az awk hoz létre egy tömböt, pl. a split beépített
függvénnyel, akkor az indexek egymás utáni egész számok lesznek egytől kezdve.
(See section Szövegmanipuláló beépített függvények.)
Egy tömb alapvető használati módja az elemeinek az elérése. Az elemek elérhetőek az alábbi formában:
array[index]
Itt az array a tömb neve. Az index kifejezés a tömb elemének eléréséhez szükséges indexet adja meg.
Egy tömb elemére való hivatkozás értéke a tömb elemének aktuális értéke.
Például a foo[4.3] kifejezés egy hivatkozás a foo tömb
`4.3' indexű elemére.
Ha egy olyan elemre hivatkozunk, aminek nincs "rögzített" értéke, akkor
az értéke az üres szöveg, "", vagyis a változónak nem adtunk
korábban értéket, vagy az elem törölve lett
(see section A delete kifejezés).
Egy ilyen hivatkozás automatikusan létrehozza az elemet egy üres szöveg
értékkel. (Néhány esetben ez nem túl szerencsés, mivel ezzel awk
memóriát pazarlunk el.)
Könnyen kideríthető, hogy egy elem az adott indexszel a tömb eleme-e:
index in array
Ez a kifejezés ellenőrzi, hogy az adott index létezik-e a tömbben, és
a kifejezésnek nincs mellékhatása, nem hozza létre az elemet. A
kifejezésnek egy (igaz) az értéke, ha az array[index]
kifejezés létezik, és zérus (hamis), ha nem létezik.
Például ha ellenőrizni akarjuk, hogy a frequencies tömbnek van-e
`2'-es indexű eleme, akkor az alábbi kifejezést használhatjuk:
if (2 in frequencies)
print "Subscript 2 is present."
Figyelem, ez a kifejezés nem azt ellenőrzi, hogy a frequencies
tömbnek van-e olyan eleme aminek az értéke kettő. (Ezt csak egyféle
képpen lehet kideríteni, ha az összes elemet átnézzük.) Ugyanakkor
a kifejezés nem hozza létre a frequencies[2] elemet, míg a
következő (helytelen) kifejezés létrehozza az elemet:
if (frequencies[2] != "")
print "Subscript 2 is present."
Egy tömb elemei `lvalue' kifejezések: így értéket adhatunk nekik, mint az awk
változóknak:
array[subscript] = value
Itt az array a tömb neve. A subscript kifejezés a tömb elemének az indexe. A value kifejezés az az érték ami a tömb elemének az új értéke lesz.
Az alábbi program egy olyan szöveget olvas be, amiben minden sor egy számmal kezdődik, majd a számok emelkedő sorrendjében kinyomtatja a sorokat. Kezdetben a számok nincsennek sorrendben. A program sorbarendezi a sorokat. A sorszámokat tömbindexként használja. Ezután kinyomtatja a sorokat a rendezett sorrendben. Ez egy nagyon egyszerű program, hibásan működik, ha egy szám kétszer szerepel, nincs megadva az összes index, vagy egy sor nem számmal kezdődik.
{
if ($1 > max)
max = $1
arr[$1] = $0
}
END {
for (x = 1; x <= max; x++)
print arr[x]
}
Az első szabály a legnagyobb számot tárolja, illetve minden sort eltárol
az arr tömbben a számmal megadott indexű elemben.
A második szabály a bemenet beolvasása után fut le, és kinyomtatja az összes sort.
Ha a programot az alábbi bemenettel futtatjuk:
5 I am the Five man 2 Who are you? The new number two! 4 . . . And four on the floor 1 Who is number one? 3 I three you.
a kimenet az alábbi lesz:
1 Who is number one? 2 Who are you? The new number two! 3 I three you. 4 . . . And four on the floor 5 I am the Five man
Ha egy szám többször szerepel, akkor az adott számmal kezdődő utolsó sor lesz eltárolva a tömbben.
Az a probléma, amikor nincs megadva minden index, könnyen elkerülhető
az END szabályban:
END {
for (x = 1; x <= max; x++)
if (x in arr)
print arr[x]
}
Az olyan programokban, amelyekben tömböket használunk, gyakran van szükség
olyan ciklusra, ami a tömb minden elemén végigmegy. Más programozási nyelvekben,
ahol a tömbök folyamatosak, és az indexek pozítiv egész számok ez nagyon könnyű:
az érvényes indexeket megkaphatjuk, ha elszámolunk
a legalacsonyabb számtól kezdve a legmagasabbig. Ez a módszer nem működik
az awk-ban, mivel bármilyen szöveg vagy szám lehet index. Így a
for kifejezésnek van egy speciális alakja, amivel egy tömb összes
eleme ellenőrizhető:
for (var in array) body
Ez a ciklus végrehajtja a body kifejezést az array tömb minden elemére egyszer, és a var változó megkapja a korábban használt indexeket.
Alább közlünk egy programot, ami a for kifejezésnek ezt a formáját
használja. Az első szabály végignézi a bemeneti rekordokat, és minden
legalább egyszer előforduló szót eltárol a used tömbben, ahol az
adott szó szolgál indexként. A második szabály végigmegy a used tömb
összes elemén, és minden 10 karakternél hosszabb szót kinyomtat, majd
kinyomtatja az ilyen szavak számát.
A section Szövegmanipuláló beépített függvények,
további információt add a length függvényről.
# Minden használt szó esetén legyen az elem értéke egy.
{
for (i = 1; i <= NF; i++)
used[$i] = 1
}
# 10 karakternél hosszabb szavakat keresünk.
END {
for (x in used)
if (length(x) > 10) {
++num_long_words
print x
}
print num_long_words, "words longer than 10 characters"
}
Lásd még a section Generating Word Usage Counts, ahol hasonló, részletesebb példák találhatók.
Egy tömb elemeinek elérési sorrendje az awk belső struktúrájától
függ, és nem lehet szabályozni vagy megváltoztatni. Ez problémákhoz vezet
ha az array tömbhöz elemeket adunk a cikluson belül; nem lehet
megjósólni, hogy a for ciklus eléri-e az új elemeket vagy sem. Ehhez
hasonló problémák lehetnek, ha a var változót a for cikluson
belül megváltoztatjuk. A legjobb az ilyen változtatásokat elkerülni.
delete kifejezés
Egy tömb elemet el lehet távolítani, törölni lehet a delete
kifejezéssel:
delete array[index]
Ha egyszer egy elem törölve lett, az értéke többé nem érhető el. Lényegében olyan, mintha soha nem hivatkoztunk volna rá, és soha nem adtunk neki értéket.
Itt egy példa a törlésre:
for (i in frequencies) delete frequencies[i]
Ez a példa kitörli az összes elemet a frequencies tömbből.
Ha törlünk egy elemet, akkor sem egy for ciklus során nem lehet az elemet
elérni, és az in operátor, ami ellenőrzi, hogy az elem létezik-e
a tömbben, szintén zérussal tér vissza (pl. hamis értékkel):
delete foo[4]
if (4 in foo)
print "Soha nem nyomtatja ezt ki"
Fontos megjegyezni, hogy egy elem törlése nem ugyanaz, mintha egy
üres szöveg, "", értéket rendelünk az elemhez.
foo[4] = "" if (4 in foo) print "Ezt kinyomtatja, még ha a foo[4] üres is"
Egy nem létező elem törlése nem jelent hibát.
Egy tömb minden eleme törölhető egy kifejezéssel, ha elhagyjuk az indexet
a delete kifejezésben.
delete array
Ez a lehetőség egy gawk kiegészítés; nem érhető el "compatibility"
módban
(see section Command Line Options).
Ha a delete kifejezést így használjuk, az kb. háromszor olyan
hatékony, mintha az elemeket egyenként törölnénk egy ciklussal.
Az alábbi kifejezés egy hordozható, de nem magától értetődő megoldást mutat egy tömb törlésére.
# köszönet Michael Brennan -nak ezért a példáért
split("", array)
A split függvény
(see section Szövegmanipuláló beépített függvények)
törli a céltömböt, mivel egy üres szöveget darabol fel. Mivel nincs mit
felvágni (nincs adat), ezért egyszerűen törli a tömböt, és visszatér.
Figyelem: Egy elem törlése nem változtatja meg a típusát, így a törlés után nem használható mint skaláris szám. Például ez nem működik:
a[1] = 3; delete a; a = 3
Tömbök esetén fontos arra emlékezni, hogy a tömbök indexei mindig szövegek. Ha egy számot használunk indexként, át lesz konvertálva szöveggé mielőtt mint index lesz használva (see section Szövegek és számok konverziója).
Ez azt jelenti, hogy a CONVFMT beépített változó befolyásolja azt,
hogy a tömbök elemeit hogyan lehet elérni. Például:
xyz = 12.153
data[xyz] = 1
CONVFMT = "%2.2f"
if (xyz in data)
printf "%s is in data\n", xyz
else
printf "%s is not in data\n", xyz
Ez kinyomtatja: `12.15 is not in data'. Az első kifejezés numerikus
értéket ad az xyz változónak. Ezután a data[xyz] kifejezésben
lényegében az index a "12.153" szöveg lesz
(az alap konverziót a CONVFMT alapján végzi el, "%.6g"),
és egyet rendel a data["12.153"] tömb elemhez. A program ezután
megváltoztatja a CONVFMT értékét. A `xyz in data' kifejezés egy
új szöveget generál a xyz változóból, ez alkalommal "12.15",
mivel a CONVFMT csak két értékes jegyet enged meg. A teszt nem lesz
sikeres, mivel a "12.15" szöveg nem egyezik meg a "12.153"
szöveggel.
A konverzió szabályai szerint
(see section Szövegek és számok konverziója),
az egész számokat mindig mint egész szám konvertálja szöveggé, attól
függetlenül, hogy mi a CONVFMT értéke. Így:
for (i = 1; i <= maxsub; i++)
valami történik itt az array[i] -vel
a program mindig működik, attól függetlenül hogy mi a CONVFMT értéke.
Mint a legtöbb dolog az awk-ban, a legtöbb esetben úgy működik,
ahogy azt elvárjuk. De fontos, hogy pontosan tudjuk, hogy milyen szabályok
érvényesek, mivel gyakran egy apró részlet nagy hatással lehet a programodra.
Tegyük fel, hogy a bemeneti sorokat fordított sorrendben akarjuk kinyomtatni. Egy ésszerű kísérlet ennek megvalósítására az alábbi program, ami valahogy így nézne ki:
$ echo 'line 1
> line 2
> line 3' | awk '{ l[lines] = $0; ++lines }
> END {
> for (i = lines-1; i >= 0; --i)
> print l[i]
> }'
-| line 3
-| line 2
Sajnos a legelső bemeneti sort nem nyomtatta ki a program!
Első pillantásra a programnak működnie kellene. A lines változó
nincs inicializálva, és egy nem inicializált változónak zérus szám értéke van.
Így az l[0] értéket kellene kinyomtatnia.
De a lényeg, hogy az awk tömbök indexei mindig szövegek. Egy
nem inicializált változó, amikor szöveg az értéke "" és nem zérus.
Így a `line 1' az l[""] -ben lesz tárolva.
A program alábbi verziója helyesen működik:
{ l[lines++] = $0 }
END {
for (i = lines - 1; i >= 0; --i)
print l[i]
}
Itt a `++' operátor arra kényszeríti a lines változót, hogy szám
típusú legyen, így a régi érték numerikus zérus lesz, ami "0" szöveggé
lesz konvertálva mint tömb index.
Amint az láttuk, bár egy kicsit szokatlan, de az üres szöveg ("") is
használható mint tömb index (s.s.). Ha a `--lint' opció szerepel
a parancssorban
(see section Command Line Options),
a gawk figyelmeztet arra, hogy egy üres szöveget akartunk indexként
használni.
Egy többdimenziós tömb egy olyan tömb, amiben az elemeket indexek sorozata
azonosít, és nem csak egy index. Például egy kétdimenziós tömb esetén két
indexre van szükség. Általában (más programozási nyelvekben, beleértve
az awk-ot is) egy kétdimenziós tömböt mátrixnak is szoktak
nevezni, pl.
matrix[x,y].
Az awk támogatja a többdimenziós tömbök használatát, indexek egy
szövegbe kapcsolásával. Lényegében az történik, hogy az awk az indexeket
szöveggé alakítja
(see section Szövegek és számok konverziója), és összekapcsolja
őket egy speciális elválasztóval. Így egyetlen szöveget kapunk.
Ezt a kombinált szöveget használja mint egy
index egy egydimenziós tömbben. Az elválasztót a SUBSEP beépített
változó értéke adja meg.
Például tegyük fel, hogy a `foo[5,12] = "value"' kifejezést
kiértékeljük, amikor a SUBSEP változó értéke "@". Az ötös
és a 12-es számot átalakítja szövegekké és összekapcsolja őket egy
`@' jellel közöttük, aminek az eredménye: "5@12". Így a
foo["5@12"] tömb eleme "value" lesz.
Ha egyszer egy tömb elemének értéket adtunk, attól kezdve nincs arra lehetőség, hogy kiderítsük, hogy egy indexet vagy több indexet használtunk. A `foo[5,12]' és a `foo[5 SUBSEP 12]' kifejezések mindig teljesen azonosak.
A SUBSEP alapértéke a "\034" szöveg, ami egy nem nyomtatható
karakter, és így nem valószínű, hogy egy awk programban vagy adatfile-ban
előfordulhat.
Egy valószínűtlen karakter használatának hasznossága abból fakad, hogy
egy index értéke ami tartalmazza a SUPSEP szöveget olyan kombinált
szöveghez vezet ami kétértelmű. Tegyük fel, hogy a SUBSEP
értéke "@", akkor a `foo["a@b", "c"]' és a
`foo["a", "b@c"]' kifejezések teljesen megkülönböztethetetlenek
mivel végül is a `foo["a@b@c"]' kifejezéssel egyeznek meg.
Ugyanazzal az `in' operátorral lehet ellenőrizni egy index létezését egy többdimenziós tömbben, mint egy egyszerű egydimenziós tömbben. Egyetlen index helyett indexek sorozatát kell leírni, vesszővel elválasztva zárójelekben:
(subscript1, subscript2, ...) in array
A következő példa a bemenetet mint egy két dimenziós tömb kezeli; elforgatja ezt a tömböt 90 fokkal az óramutató járásával egyező irányban, és kinyomtatja az eredményt. A program feltételezi, hogy minden sor azonos számú elemet tartalmaz.
awk '{
if (max_nf < NF)
max_nf = NF
max_nr = NR
for (x = 1; x <= NF; x++)
vector[x, NR] = $x
}
END {
for (x = 1; x <= max_nf; x++) {
for (y = max_nr; y >= 1; --y)
printf("%s ", vector[x, y])
printf("\n")
}
}'
Amikor az alábbi az input:
1 2 3 4 5 6 2 3 4 5 6 1 3 4 5 6 1 2 4 5 6 1 2 3
a végeredmény:
4 3 2 1 5 4 3 2 6 5 4 3 1 6 5 4 2 1 6 5 3 2 1 6
Nincs speciális for kifejezés "többdimenziós" tömbök
eléréséhez; nem is lehet, hiszen igazából nincsenek is többdimenziós
tömbök; csak többdimenziós tömb elérési mód van.
Ugyanakkor, ha a programod mindig mint több dimenziós tömb használja a
tömböt, akkor azt a hatást kelthetjük ha a for kifejezést
(see section Egy tömb elemeinek ellenőrzése)
kombináljuk a split beépített függvénnyel
(see section Szövegmanipuláló beépített függvények).
Így működik:
for (combined in array) {
split(combined, separate, SUBSEP)
...
}
Ez beállítja a combined változót minden összefűzőtt indexre, és
feldarabolja egyedi indexekre, ott ahol a SUBSEP értékének
megfelelő szöveg található. A feldarabolt indexek a separate
tömb elemei lesznek.
Így, ha korábban egy értéket tároltunk az array[1, "foo"] -ban, akkor
létezik egy elem az array tömbben "1\034foo" indexel.
(A SUBSEP alapértéke egy 034 -es kódú karakter.) Előbb vagy utóbb
a for ciklus megtalálja ezt az elemet, és combined értéke a
"1\034foo" lesz. Ezután a split függvényt hívja meg:
split("1\034foo", separate, "\034")
Ennek az eredménye, hogy a separate[1] értéke "1" lesz és
a separate[2] értéke "foo". Vagyis visszakaptuk az
eredeti indexsorozatot.
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.
(Te is definiálhatsz függvényeket.
See section Felhasználó által definiált függvények.)
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.
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 "]").
int(x)
int(3) eredménye három, int(3.9) eredménye három,
int(-3.9) eredménye -3 és int(-3) eredménye szintén
-3.
sqrt(x)
sqrt(4) eredménye kettő.
exp(x)
e ^ x), vagy hibát jelez ha x az értelmezési
tartományon kívül esik. Az x értelmezési tartománya
a géped lebegőpontos ábrázoló képességétől függ.
log(x)
sin(x)
cos(x)
atan2(y, x)
y / x (radián)
inverz tangensét (arctan).
rand()
rand által
visszaadott értékek egyenletesen oszlanak el egy és zérus között.
Az érték viszont soha nem zérus és nem egy.
Gyakran véletlen egész számokra van szükséged. Itt egy felhasználó által
definiálható függvény, ami véletlen, nem negatív, n-nél kisebb
egész számokat generál:
function randint(n) {
return int(n * rand())
}
A szorzás egy zérusnál nagyobb, és n-nél kisebb véletlen valós számot
generál. Ezután egész számmá konvertáljuk (az int függvényt használva),
ami zérus és n - 1 közé esik, a határokat is beleértve.
Itt egy másik példa, ahol hasonló függvényt használunk egy és n közé
eső számok generálására. Ez a program minden bemeneti rekordnál egy új
véletlen számot nyomtat ki.
awk '
# Szimulált kockadobás
function roll(n) { return 1 + int(rand() * n) }
# 3 db hat-oldalú kockával dobunk és
# a dobott pontok összegét nyomtatjuk ki.
{
printf("%d points\n",
roll(6)+roll(6)+roll(6))
}'
Figyelem: A legtöbb awk implementációban,
a gawk-ot is beleértve, a rand függvény ugyanattól a
számtól (seed) kezd el véletlen számokat generálni, amikor
az awk újraindul. Így a program minden alkalommal
ugyanazt az eredményt fogja produkálni. A számok véletlenszerűek
egy awk futtatás alatt, de megjósolhatók egymás utáni futtatások során.
Ez hasznos lehet tesztelésnél, de ha azt akarod, hogy a programod
minden futtatás során más számot generáljon, akkor más kezdő
számot (seed) kell megadni minden alkalommal. Ezt az srand függvénnyel
lehet elérni.
srand([x])
srand függvény a véletlen szám generálás kezdőpontját (seed)
állítja be az x által megadott értékkel.
Minden kezdőpont más véletlen számsorozathoz vezet.(11)
Így ha a kezdőpont ugyanaz egy második alkalommal, akkor ugyanazt a sorozatot
fogod megkapni, mint az első alkalommal.
Ha nem adsz meg argumentumot és csak az srand() formát használod,
akkor az aktuális dátumot és időpontot használja kezdőpontként. Ez a módja
annak, hogy igazi véletlen számokat generáljunk.
Az srand függvény visszatérési értéke az előző kezdőpont értéke.
Ez lehetővé teszi a megadott kezdőpontok nyilvántartását és ugyanazon
véletlen szám sorozatok generálását.
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.
index(in, find)
$ awk 'BEGIN { print index("peanut", "an") }'
-| 3
Ha nem találja a find szöveget, akkor az index függvény
zérussal tér vissza. (Az awk szövegek első karaktere az egyes
pozíciónál található.)
length([string])
length("abcde") visszatérési értéke öt.
Ezzel ellentétben, az length(15 * 35) eredménye három lesz. Miért?
Nos, 15 * 35 = 525, amit szöveggé konvertál "525", amiben három karakter
van.
Ha nincs argumentum megadva, akkor a length függvény a $0
hosszát adja meg.
Az awk régebbi verzióiban a length függvényt zárójelek nélkül
is meg lehetett hívni. A POSIX szabvány ezt "helyteleníti". Ez azt jelenti,
hogy ez a lehetőség végül is el fog tűnni a szabványból. Így a maximális
hordozhatóság érdekében a legjobb a zárójeleket kitenni.
match(string, regexp)
match függvény a string szövegben a leghosszabb,
legbaloldalibb szövegrészletet keresi meg, ami illeszkedik a regexp
reguláris kifejezésre. Azt a karakterpozíciót adja vissza, vagy indexet,
ahol a szövegrészlet kezdődik (ez egy, ha a string elején kezdődik).
Ha nem volt illeszkedés, akkor a visszatérési érték zérus.
A match függvény az RSTART beépített változónak az indexet
adja meg, mint új érték. Ezen kívül az RLENGTH beépített változó
az illeszkedő szöveg hosszát adja meg. Ha nem volt illeszkedés, az
RSTART értéke zérus, az RLENGTH értéke -1 lesz.
Például:
awk '{
if ($1 == "FIND")
regex = $2
else {
where = match($0, regex)
if (where != 0)
print "Match of", regex, "found at", \
where, "in", $0
}
}'
Ez a program olyan sorokat keres, amelyek illeszkednek a regex változóban
tárolt kifejezésre. Ez a reguláris kifejezés megváltoztatható, így ha a sorban
az első szó a `FIND', akkor a regex új értéke a sor második szava lesz.
Így ha ez a bemenet:
FIND ru+n My program runs but not very quickly FIND Melvin JF+KM This line is property of Reality Engineering Co. Melvin was here.az
awk ezt fogja kinyomtatni:
Match of ru+n found at 12 in My program runs Match of Melvin found at 1 in Melvin was here.
split(string, array [, fieldsep])
array[1] -ba teszi, a másodikat a array[2]-ba
és így tovább. A harmadik argumentum, a fieldsep, egy reguláris kifejezés
ami azt adja meg, hogy hol kell a string szöveget felvágni (ugyanúgy mint
ahogy az FS azt adja meg, hogy hol kell a mezőket feldarabolni).
Ha a fieldsep nincs megadva, akkor az FS értékét használja.
A split függvény visszatérési értéke a generált szöveg darabok száma.
A split függvény a bemeneti rekord mezőkre darabolásához hasonló módon
vágja fel a szöveget. Például:
split("cul-de-sac", a, "-")
felvágja a `cul-de-sac' szöveget három darabba a `-' jel mentén.
Az a tömb tartalma a következő lesz:
a[1] = "cul" a[2] = "de" a[3] = "sac"A
split függvény visszatérési értéke három lesz.
Ugyanúgy mint a meződarabolásnál, amikor a fieldsep értéke " ",
akkor a kezdő és záró szóközöket és tab karaktereket nem veszi figyelembe és
az elemeket szóközök és tab karakterek (sorozatai) választhatják el.
Szintén igaz, hogy ha a fieldsep értéke egy üres szöveg, akkor a szöveg
minden karaktere egy külön tömb elembe kerül. (Ez egy gawk
specifikus kiterjesztés.)
Az awk újabb implementációi, a gawk is, megengedi, hogy
a harmadik argumentum egy regexp konstans (/abc/) vagy egy szöveg
legyen (s.s.). A POSIX szabvány szintén megengedi ezt.
Mielőtt a szöveget felvágná, a split függvény kitörli az array
tömb minden létező elemét (s.s.).
Ha a string szöveg egyik részlete sem illeszkedik a fieldsep
szövegre, akkor az array tömbnek csak egy eleme lesz. Az elem
értéke maga az eredeti szöveg, a string, lesz.
sprintf(format, expression1,...)
printf függvény nyomtatna ki ugyanazokkal az argumentumokkal
(see section Nyomtatás a printf kifejezéssel).
Például:
sprintf("pi = %.2f (approx.)", 22/7)
a visszatérési értéke a "pi = 3.14 (approx.)" lesz.
sub(regexp, replacement [, target])
sub megváltoztatja a target értékét. A függvény a
regexp reguláris kifejezésre illeszkedő legbaloldalibb, leghosszabb
illeszkedést keresi a target-en belül. Ezután az egész szöveget
megváltoztatja úgy, hogy az illeszkedő szöveg részletet lecseréli a
replacement szövegre. A módosított szöveg lesz a target
új értéke.
Ez a függvény különleges, mert a target értékét nem csak egy új
érték "kiszámításához" használja, hanem meg is változtatja.
Csak bizonyos kifejezések állhatnak a target helyén, úgy mint változók, mezők vagy
tömb elemek, ahova a sub el tudja tárolni a módosított értéket.
Ha ez az argumentum nincs megadva, akkor az alapérték a $0.
Például:
str = "water, water, everywhere" sub(/at/, "ith", str)az
str új értéke a "wither, water, everywhere" lesz,
mivel lecseréli az `at' legbaloldalibb, leghosszabb előfordulását
a `ith' szövegre.
A sub függvény az elvégzett behelyettesítések száma lesz (vagy egy
vagy zérus).
Ha a `&' speciális karakter előfordul a replacement-ben,
akkor ez a regexp-re illeszkedő szöveg részletet jelenti. (Ha regexp
egynél több szövegre illeszkedik, akkor ez a szöveg részlet változhat.)
Például:
awk '{ sub(/candidate/, "& and his wife"); print }'
a `candidate' első előfordulását lecseréli a `candidate
and his wife' szövegre a bemeneti file minden sorában.
Itt egy másik példa:
awk 'BEGIN {
str = "daabaaa"
sub(/a*/, "c&c", str)
print str
}'
-| dcaacbaaa
Ez azt mutatja be, hogy a `&' hogyan képes nem konstans szöveget
reprezentálni, és bemutatja, hogy mit jelent a "legbaloldalibb, leghosszabb"
szabály a reguláris kifejezések illesztésében
(see section Mennyi szöveg illeszkedik?).
A `&' speciális karakter hatása kikapcsolható, ha egy `\' karaktert
teszünk eléje. Mint mindig, ha egy szövegbe akarsz egy `\' karaktert
írni, akkor kettőt kell belőle leírni. Tehát a `\\&' karaktereket
kell egy szöveg konstansba beírni, hogy maga a `&' karakter
jelenjen meg a csere után. Például, itt bemutatjuk, hogy hogyan lehet az első
`|' karaktert lecserélni egy `&' karakterre minden sorban:
awk '{ sub(/\|/, "\\&"); print }'
Megjegyzés: Ahogy azt korábban írtuk a sub harmadik
argumentuma egy változó, egy mező vagy egy tömb eleme kell legyen. Az
awk néhány verziója megengedi, hogy a harmadik argumentum ne egy
lvalue legyen. Ebben az esetben a sub ugyanúgy viselkedik, de
a csere eredményét eldobja, mivel nincs hol tárolnia. Ezek az awk verziók
elfogadnák az alábbi kifejezést is:
sub(/USA/, "United States", "the USA and Canada")A történelmi kompatibilitás miatt a
gawk elfogadja a fenti hibás kódot is,
de bármilyen "nem megváltoztatható" objektum, mint harmadik argumentum esetén
fatális hibával leáll.
Végül, ha a regexp nem egy reguláris kifejezés, akkor egy szöveggé konvertálja
majd a szöveg értékét használja regexp-ként az illesztésben.
gsub(regexp, replacement [, target])
sub függvényhez, kivéve, hogy ez a függvény
minden legbaloldalibb, leghosszabb, nem átfedő, illeszkedő
szöveg részletet lecserél. A `g' a gsub névben a "globálist"
jelenti, vagyis cseréljen mindenhol. Például:
awk '{ gsub(/Britain/, "United Kingdom"); print }'
a `Britain' szöveg minden előfordulását lecseréli a `United
Kingdom' szövegre a bemeneti rekordokban.
A gsub függvény az elvégzett cserék számával tér vissza. Ha a harmadik
argumentum nincs megadva, akkor a keresést és a cserét a bemeneti rekordon,
$0, végzi el.
Ugyanúgy mint a sub-nál a `&' és `\' karakterek speciálisak,
és a harmadik argumentum egy lvalue kell legyen.
gensub(regexp, replacement, how [, target])
gensub egy általános helyettesítő függvény. Mint a sub és a
gsub a target szövegben a regexp kifejezésre illeszkedő szöveg
darabot keres. Ugyanakkor a sub és a gsub függvényekkel
ellentétben a módosított szöveg a függvény visszatérési értéke lesz, és az
eredeti target szöveg nem lesz módosítva. Ha a how egy
olyan szöveg ami a `g' vagy `G' karakterrel kezdődik,
akkor az összes illeszkedő szövegdarabot lecseréli a replacement
szövegre. Minden más esetben, a how egy szám kell legyen, ami
azt adja meg, hogy hanyadik illeszkedést kell lecserélni. Ha a target
nincs megadva, akkor a $0-t használja.
A gensub függvénynek van egy olyan szolgáltatása is, ami nem használható
sem a sub sem a gsub függvényekkel: a reguláris kifejezés
komponenseinek felhasználása a csereként szolgáló szövegben. Ezt úgy lehet
elérni, hogy zárójelek közé kell tenni a regexp adott komponensét és a
csereként szolgáló szövegben a `\n' kifejezést kell használni,
ahol az n egy egy és kilenc közé eső szám. Például:
$ gawk '
> BEGIN {
> a = "abc def"
> b = gensub(/(.+) (.+)/, "\\2 \\1", "g", a)
> print b
> }'
-| def abc
Ahogy azt a sub függvénynél elmagyaráztuk, két `\' karaktert kell
leírni ahhoz, hogy egy `\' karakter bekerüljön a szövegbe.
A csereként szolgáló szövegben a `\0' karakter sorozat a teljes
illeszkedő szöveget jelenti, ugyanúgy mint a `&' karakter.
Ez a példa bemutatja, hogy hogyan szabályozza a harmadik argumentum
azt, hogy melyik illeszkedés lesz lecserélve.
$ echo a b c a b c |
> gawk '{ print gensub(/a/, "AA", 2) }'
-| a b c AA b c
Ebben az esetben a $0 lesz a negyedik argumentum. A gensub
az új, csere utáni szöveggel tér vissza, amit átad a print-nek,
hogy nyomtassa ki.
Ha a how argumentum egy olyan szöveg, ami nem `g' vagy `G'
karakterekkel kezdődik, vagy egy zérusnál kisebb szám, akkor csak egy
helyettesítés hajtódik végre.
Ha a regexp nem illeszkedik a target egyik részére sem, akkor a
gensub visszatérési értéke az eredeti, változatlan target szöveg lesz.
A gensub egy gawk kiegészítés és "compatibility" módban
nem használható (see section Command Line Options).
substr(string, start [, length])
substr("washington", 5, 3) eredménye az "ing".
Ha a length nincs megadva, akkor a függvény a string szövegnek a
start karakterpozíciónál kezdődő és a szöveg végéig tartó részszövegével
tér vissza. Például, substr("washington", 5) eredménye az "ington".
Ugyanez történik ha a length nagyobb hosszat ad meg, mint ami a start
karakterpozíciótól a szöveg végéig tart.
Megjegyzés: A substr függvény által visszaadott
szövegnek nem lehet értéket adni. Így az alábbi kísérlet
a szöveg egy részének megváltoztatására hibás:
string = "abcdef" # ezt szeretnénk "abCDEf", de nem fog menni ! substr(string, 3, 3) = "CDE"A
substr függvényt nem lehet használni a sub vagy a gsub
függvények harmadik argumentumában sem:
gsub(/xyz/, "pdq", substr($0, 5, 20)) # HIBÁS
tolower(string)
tolower("MiXeD cAsE 123") eredménye a "mixed case 123".
toupper(string)
tolower("MiXeD cAsE 123") eredménye a "MIXED CASE 123".
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
(section Escape szekvenciák). Í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.
awk programok nem fognak helyesen működni.
awk program hordozható,
akkor a replacement szövegben minden karakter elé egy
`\' karaktert kell tenni.(12)
A POSIX szabvány átdolgozás alatt áll.(13)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.
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.
close(filename)
fflush([filename])
fflush függvény. A gawk is buffereli a
kimenetét, és az fflush függvénnyel lehet arra kényszeríteni, hogy
az adott pillanatban a buffer tartalmát kiírja.
Az fflush függvényt 1994-ben adták hozzá a Bell laboratóriumban
fejlesztett awk verzióhoz; a függvény nem része a POSIX szabványnak,
és nem használható ha a `--posix' opció szerepel a parancssorban
(see section Command Line Options).
A gawk kétféle módon fejlesztette tovább az fflush függvényt.
Az első, hogy argumentum nélkül is meg lehet hívni a függvényt. Ebben
az esetben a szabványos kimenet lesz ürítve. A második, hogy az üres
szöveg ("") is megadható argumentumként, amikor is
minden megnyitott file vagy cső buffere ürítve lesz.
Az fflush függvény zérus értékkel tér vissza ha sikeres volt
az ürítés, és egy nem zérus értékkel minden más esetben.
system(command)
system függvény teszi lehetővé, hogy a felhasználó bármilyen
az operációs rendszerben engedélyezett parancsot végrehajthasson, majd
az awk program futása folytatódjon. A system függvény
a command szövegben adott parancsot hajtja végre. A parancs
végrehajtása által visszaadott érték lesz a függvény visszatérési értéke.
Például, ha az alábbi programrészletet teszed be az awk programodba:
END {
system("date | mail -s 'awk run done' root")
}
akkor a rendszeradminisztrátor egy levelet fog kapni, amikor az
awk program befejezte a bemenete feldolgozását.
A print vagy a printf átirányítása egy csőbe gyakran elég
a feladat elvégzéséhez. Ha sok parancsot kell végrehajtani, akkor
hatékonyabb egy csőbe nyomtatni, ami egy shell-be van átirányítva:
while (amíg van mit csinálni)
print command | "/bin/sh"
close("/bin/sh")
Ugyanakkor ha az awk programod a felhasználótól is vár bemenetet
(interaktív), akkor a system függvény jól használható nagyobb
programok indítására, mint egy shell vagy egy szövegszerkesztő.
Néhány operációs rendszer nem támogatja a system függvény használatát,
ebben az esetben fatális hibát okoz a függvény hívása.
É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.(14)
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.
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.
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.
systime()
strftime([format [, timestamp]])
systime függvény
visszaadna. Ha nincs megadva a timestamp argumentum, akkor a
gawk az aktuális időt veszi alapul. Ha a format argumentum
nincs megadva, akkor az strftime függvény a
"%a %b %d %H:%M:%S %Z %Y" szöveget fogja használni.
Ez a formátum (majdnem) ugyanaz mint amit a date segédprogram
használ. (A gawk 3.0-ás verziója előtti verziókban a format
argumentumot mindig meg kellett adni.)
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
(see section Szövegmanipuláló beépített függvények),
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:
%a
%A
%b
%B
%c
%d
%H
%I
%j
%m
%M
%p
%S
%U
%w
%W
%x
%X
%y
%Y
%Z
%%
Ha a formátum leíró nem a fentiek közül valamelyik, akkor a viselkedés nem definiált.(16)
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
(see section Installing gawk), akkor az alábbi
formátumleírókat is lehet használni:
%D
%e
%h
%n
%r
%R
%T
%t
%k
%l
%C
%u
%V
%G
%g
%Ec %EC %Ex %Ey %EY %Od %Oe %OH %OI
%Om %OM %OS %Ou %OU %OV %Ow %OW %Oy
date segédprogrammal
kompatíbilis.)
%v
%z
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
}' "$@"
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.
awk
There are two ways to run awk: with an explicit program, or with
one or more program files. Here are templates for both of them; items
enclosed in `[...]' in these templates are optional.
Besides traditional one-letter POSIX-style options, gawk also
supports GNU long options.
awk [options] -f progfile [--] file ... awk [options] [--] 'program' file ...
It is possible to invoke awk with an empty program:
$ awk '' datafile1 datafile2
Doing so makes little sense though; awk will simply exit
silently when given an empty program (d.c.). If `--lint' has
been specified on the command line, gawk will issue a
warning that the program is empty.
Options begin with a dash, and consist of a single character. GNU style long options consist of two dashes and a keyword. The keyword can be abbreviated, as long the abbreviation allows the option to be uniquely identified. If the option takes an argument, then the keyword is either immediately followed by an equals sign (`=') and the argument's value, or the keyword and the argument's value are separated by whitespace. For brevity, the discussion below only refers to the traditional short options; however the long and short options are interchangeable in all contexts.
Each long option for gawk has a corresponding
POSIX-style option. The options and their meanings are as follows:
-F fs
--field-separator fs
FS variable to fs
(see section Hogyan történik a mezőelválasztás).
-f source-file
--file source-file
awk program is to be found in source-file
instead of in the first non-option argument.
-v var=val
--assign var=val
BEGIN rule
(see section Other Command Line Arguments).
The `-v' option can only set one variable, but you can use
it more than once, setting another variable each time, like this:
`awk -v foo=1 -v bar=2 ...'.
-mf NNN
-mr NNN
awk. They are provided
for compatibility, but otherwise ignored by
gawk, since gawk has no predefined limits.
-W gawk-opt
--
The following gawk-specific options are available:
-W traditional
-W compat
--traditional
--compat
awk language are disabled, so that gawk behaves just
like the Bell Labs research version of Unix awk.
`--traditional' is the preferred form of this option.
See section Extensions in gawk Not in POSIX awk,
which summarizes the extensions. Also see
section Downward Compatibility and Debugging.
-W copyleft
-W copyright
--copyleft
--copyright
gawk.
-W help
-W usage
--help
--usage
gawk accepts, and then exit.
-W lint
--lint
awk implementations.
Some warnings are issued when gawk first reads your program. Others
are issued at run-time, as your program executes.
-W lint-old
--lint-old
awk
(see section Major Changes between V7 and SVR3.1).
-W posix
--posix
gawk
extensions (just like `--traditional'), and adds the following additional
restrictions:
\x escape sequences are not recognized
(see section Escape szekvenciák).
FS is
equal to a single space.
func for the keyword function is not
recognized (see section A függvénydefiníció formája).
FS to be a single tab character
(see section Hogyan történik a mezőelválasztás).
fflush built-in function is not supported
(see section Bemenettel és kimenettel (I/O) kapcsolatos beépített függvények).
gawk
will also issue a warning if both options are supplied.
-W re-interval
--re-interval
awk,
gawk does not provide them by default. This prevents old awk
programs from breaking.
-W source program-text
--source program-text
AWKPATH Environment Variable).
-W version
--version
gawk.
This allows you to determine if your copy of gawk is up to date
with respect to whatever the Free Software Foundation is currently
distributing.
It is also useful for bug reports
(see section Reporting Problems and Bugs).
Any other options are flagged as invalid with a warning message, but are otherwise ignored.
In compatibility mode, as a special case, if the value of fs supplied
to the `-F' option is `t', then FS is set to the tab
character ("\t"). This is only true for `--traditional', and not
for `--posix'
(see section Hogyan történik a mezőelválasztás).
The `-f' option may be used more than once on the command line.
If it is, awk reads its program source from all of the named files, as
if they had been concatenated together into one big file. This is
useful for creating libraries of awk functions. Useful functions
can be written once, and then retrieved from a standard place, instead
of having to be included into each individual program.
You can type in a program at the terminal and still use library functions,
by specifying `-f /dev/tty'. awk will read a file from the terminal
to use as part of the awk program. After typing your program,
type Control-d (the end-of-file character) to terminate it.
(You may also use `-f -' to read program source from the standard
input, but then you will not be able to also use the standard input as a
source of data.)
Because it is clumsy using the standard awk mechanisms to mix source
file and command line awk programs, gawk provides the
`--source' option. This does not require you to pre-empt the standard
input for your source code, and allows you to easily mix command line
and library source code
(see section The AWKPATH Environment Variable).
If no `-f' or `--source' option is specified, then gawk
will use the first non-option command line argument as the text of the
program source code.
If the environment variable POSIXLY_CORRECT exists,
then gawk will behave in strict POSIX mode, exactly as if
you had supplied the `--posix' command line option.
Many GNU programs look for this environment variable to turn on
strict POSIX mode. If you supply `--lint' on the command line,
and gawk turns on POSIX mode because of POSIXLY_CORRECT,
then it will print a warning message indicating that POSIX
mode is in effect.
You would typically set this variable in your shell's startup file. For a Bourne compatible shell (such as Bash), you would add these lines to the `.profile' file in your home directory.
POSIXLY_CORRECT=true export POSIXLY_CORRECT
For a csh compatible shell,(18)
you would add this line to the `.login' file in your home directory.
setenv POSIXLY_CORRECT true
Any additional arguments on the command line are normally treated as
input files to be processed in the order specified. However, an
argument that has the form var=value, assigns
the value value to the variable var---it does not specify a
file at all.
All these arguments are made available to your awk program in the
ARGV array (see section Beépített változók). Command line options
and the program text (if present) are omitted from ARGV.
All other arguments, including variable assignments, are
included. As each element of ARGV is processed, gawk
sets the variable ARGIND to the index in ARGV of the
current element.
The distinction between file name arguments and variable-assignment
arguments is made when awk is about to open the next input file.
At that point in execution, it checks the "file name" to see whether
it is really a variable assignment; if so, awk sets the variable
instead of reading a file.
Therefore, the variables actually receive the given values after all
previously specified files have been read. In particular, the values of
variables assigned in this fashion are not available inside a
BEGIN rule
(see section A BEGIN és az END speciális minták),
since such rules are run before awk begins scanning the argument list.
The variable values given on the command line are processed for escape sequences (d.c.) (see section Escape szekvenciák).
In some earlier implementations of awk, when a variable assignment
occurred before any file names, the assignment would happen before
the BEGIN rule was executed. awk's behavior was thus
inconsistent; some command line assignments were available inside the
BEGIN rule, while others were not. However,
some applications came to depend
upon this "feature." When awk was changed to be more consistent,
the `-v' option was added to accommodate applications that depended
upon the old behavior.
The variable assignment feature is most useful for assigning to variables
such as RS, OFS, and ORS, which control input and
output formats, before scanning the data files. It is also useful for
controlling state if multiple passes are needed over a data file. For
example:
awk 'pass == 1 { pass 1 stuff }
pass == 2 { pass 2 stuff }' pass=1 mydata pass=2 mydata
Given the variable assignment feature, the `-F' option for setting
the value of FS is not
strictly necessary. It remains for historical compatibility.
AWKPATH Environment Variable
The previous section described how awk program files can be named
on the command line with the `-f' option. In most awk
implementations, you must supply a precise path name for each program
file, unless the file is in the current directory.
But in gawk, if the file name supplied to the `-f' option
does not contain a `/', then gawk searches a list of
directories (called the search path), one by one, looking for a
file with the specified name.
The search path is a string consisting of directory names
separated by colons. gawk gets its search path from the
AWKPATH environment variable. If that variable does not exist,
gawk uses a default path, which is
`.:/usr/local/share/awk'.(19) (Programs written for use by
system administrators should use an AWKPATH variable that
does not include the current directory, `.'.)
The search path feature is particularly useful for building up libraries
of useful awk functions. The library files can be placed in a
standard directory that is in the default path, and then specified on
the command line with a short file name. Otherwise, the full file name
would have to be typed for each file.
By using both the `--source' and `-f' options, your command line
awk programs can use facilities in awk library files.
See section A Library of awk Functions.
Path searching is not done if gawk is in compatibility mode.
This is true for both `--traditional' and `--posix'.
See section Command Line Options.
Note: if you want files in the current directory to be found, you must include the current directory in the path, either by including `.' explicitly in the path, or by writing a null entry in the path. (A null entry is indicated by starting or ending the path with a colon, or by placing two colons next to each other (`::').) If the current directory is not included in the path, then files cannot be found in the current directory. This path search mechanism is identical to the shell's.
Starting with version 3.0, if AWKPATH is not defined in the
environment, gawk will place its default search path into
ENVIRON["AWKPATH"]. This makes it easy to determine
the actual search path gawk will use.
This section describes features and/or command line options from
previous releases of gawk that are either not available in the
current version, or that are still supported but deprecated (meaning that
they will not be in the next release).
For version 3.0.4 of gawk, there are no
command line options
or other deprecated features from the previous version of gawk.
This section
is thus essentially a place holder,
in case some option becomes obsolete in a future version of gawk.
Use the Source, Luke! Obi-Wan
This section intentionally left blank.
gawkFS
(see section Command Line Options)
is not necessary given the command line variable
assignment feature; it remains only for backwards compatibility.
gawk
than you would get on a system without those files. When gawk
interprets these files internally, it synchronizes output to the
standard output with output to `/dev/stdout', while on a system
with those files, the output is actually to different open files
(see section Speciális file nevek gawk-ban).
awk Functions
This chapter presents a library of useful awk functions. The
sample programs presented later
(see section Practical awk Programs)
use these functions.
The functions are presented here in a progression from simple to complex.
section Extracting Programs from Texinfo Source Files,
presents a program that you can use to extract the source code for
these example library functions and programs from the Texinfo source
for this könyv.
(This has already been done as part of the gawk distribution.)
If you have written one or more useful, general purpose awk functions,
and would like to contribute them for a subsequent edition of this könyv,
please contact the author. See section Reporting Problems and Bugs,
for information on doing this. Don't just send code, as you will be
required to either place your code in the public domain,
publish it under the GPL (see section GNU GENERAL PUBLIC LICENSE),
or assign the copyright in it to the Free Software Foundation.
gawk-specific Features
The programs in this chapter and in
section Practical awk Programs,
freely use features that are specific to gawk.
This section briefly discusses how you can rewrite these programs for
different implementations of awk.
Diagnostic error messages are sent to `/dev/stderr'.
Use `| "cat 1>&2"' instead of `> "/dev/stderr"', if your system
does not have a `/dev/stderr', or if you cannot use gawk.
A number of programs use nextfile
(see section A nextfile kifejezés),
to skip any remaining input in the input file.
section Implementing nextfile as a Function,
shows you how to write a function that will do the same thing.
Finally, some of the programs choose to ignore upper-case and lower-case
distinctions in their input. They do this by assigning one to IGNORECASE.
You can achieve the same effect by adding the following rule to the
beginning of the program:
# ignore case
{ $0 = tolower($0) }
Also, verify that all regexp and string constants used in comparisons only use lower-case letters.
nextfile as a Function
The nextfile statement presented in
section A nextfile kifejezés,
is a gawk-specific extension. It is not available in other
implementations of awk. This section shows two versions of a
nextfile function that you can use to simulate gawk's
nextfile statement if you cannot use gawk.
Here is a first attempt at writing a nextfile function.
# nextfile --- skip remaining records in current file
# this should be read in before the "main" awk program
function nextfile() { _abandon_ = FILENAME; next }
_abandon_ == FILENAME { next }
This file should be included before the main program, because it supplies
a rule that must be executed first. This rule compares the current data
file's name (which is always in the FILENAME variable) to a private
variable named _abandon_. If the file name matches, then the action
part of the rule executes a next statement, to go on to the next
record. (The use of `_' in the variable name is a convention.
It is discussed more fully in
section Naming Library Function Global Variables.)
The use of the next statement effectively creates a loop that reads
all the records from the current data file.
Eventually, the end of the file is reached, and
a new data file is opened, changing the value of FILENAME.
Once this happens, the comparison of _abandon_ to FILENAME
fails, and execution continues with the first rule of the "real" program.
The nextfile function itself simply sets the value of _abandon_
and then executes a next statement to start the loop
going.(20)
This initial version has a subtle problem. What happens if the same data file is listed twice on the command line, one right after the other, or even with just a variable assignment between the two occurrences of the file name?
In such a case,
this code will skip right through the file, a second time, even though
it should stop when it gets to the end of the first occurrence.
Here is a second version of nextfile that remedies this problem.
# nextfile --- skip remaining records in current file
# correctly handle successive occurrences of the same file
# Arnold Robbins, arnold@gnu.org, Public Domain
# May, 1993
# this should be read in before the "main" awk program
function nextfile() { _abandon_ = FILENAME; next }
_abandon_ == FILENAME {
if (FNR == 1)
_abandon_ = ""
else
next
}
The nextfile function has not changed. It sets _abandon_
equal to the current file name and then executes a next satement.
The next statement reads the next record and increments FNR,
so FNR is guaranteed to have a value of at least two.
However, if nextfile is called for the last record in the file,
then awk will close the current data file and move on to the next
one. Upon doing so, FILENAME will be set to the name of the new file,
and FNR will be reset to one. If this next file is the same as
the previous one, _abandon_ will still be equal to FILENAME.
However, FNR will be equal to one, telling us that this is a new
occurrence of the file, and not the one we were reading when the
nextfile function was executed. In that case, _abandon_
is reset to the empty string, so that further executions of this rule
will fail (until the next time that nextfile is called).
If FNR is not one, then we are still in the original data file,
and the program executes a next statement to skip through it.
An important question to ask at this point is: "Given that the
functionality of nextfile can be provided with a library file,
why is it built into gawk?" This is an important question. Adding
features for little reason leads to larger, slower programs that are
harder to maintain.
The answer is that building nextfile into gawk provides
significant gains in efficiency. If the nextfile function is executed
at the beginning of a large data file, awk still has to scan the entire
file, splitting it up into records, just to skip over it. The built-in
nextfile can simply close the file immediately and proceed to the
next one, saving a lot of time. This is particularly important in
awk, since awk programs are generally I/O bound (i.e.
they spend most of their time doing input and output, instead of performing
computations).
When writing large programs, it is often useful to be able to know
that a condition or set of conditions is true. Before proceeding with a
particular computation, you make a statement about what you believe to be
the case. Such a statement is known as an
"assertion." The C language provides an <assert.h> header file
and corresponding assert macro that the programmer can use to make
assertions. If an assertion fails, the assert macro arranges to
print a diagnostic message describing the condition that should have
been true but was not, and then it kills the program. In C, using
assert looks this:
#include <assert.h>
int myfunc(int a, double b)
{
assert(a <= 5 && b >= 17);
...
}
If the assertion failed, the program would print a message similar to this:
prog.c:5: assertion failed: a <= 5 && b >= 17
The ANSI C language makes it possible to turn the condition into a string for use
in printing the diagnostic message. This is not possible in awk, so
this assert function also requires a string version of the condition
that is being tested.
# assert --- assert that a condition is true. Otherwise exit.
# Arnold Robbins, arnold@gnu.org, Public Domain
# May, 1993
function assert(condition, string)
{
if (! condition) {
printf("%s:%d: assertion failed: %s\n",
FILENAME, FNR, string) > "/dev/stderr"
_assert_exit = 1
exit 1
}
}
END {
if (_assert_exit)
exit 1
}
The assert function tests the condition parameter. If it
is false, it prints a message to standard error, using the string
parameter to describe the failed condition. It then sets the variable
_assert_exit to one, and executes the exit statement.
The exit statement jumps to the END rule. If the END
rules finds _assert_exit to be true, then it exits immediately.
The purpose of the END rule with its test is to
keep any other END rules from running. When an assertion fails, the
program should exit immediately.
If no assertions fail, then _assert_exit will still be
false when the END rule is run normally, and the rest of the
program's END rules will execute.
For all of this to work correctly, `assert.awk' must be the
first source file read by awk.
You would use this function in your programs this way:
function myfunc(a, b)
{
assert(a <= 5 && b >= 17, "a <= 5 && b >= 17")
...
}
If the assertion failed, you would see a message like this:
mydata:1357: assertion failed: a <= 5 && b >= 17
There is a problem with this version of assert, that it may not
be possible to work around with standard awk.
An END rule is automatically added
to the program calling assert. Normally, if a program consists
of just a BEGIN rule, the input files and/or standard input are
not read. However, now that the program has an END rule, awk
will attempt to read the input data files, or standard input
(see section Kezdő és lezáró tevékenységek),
most likely causing the program to hang, waiting for input.
The way printf and sprintf
(see section Nyomtatás a printf kifejezéssel)
do rounding will often depend
upon the system's C sprintf subroutine.
On many machines,
sprintf rounding is "unbiased," which means it doesn't always
round a trailing `.5' up, contrary to naive expectations. In unbiased
rounding, `.5' rounds to even, rather than always up, so 1.5 rounds to
2 but 4.5 rounds to 4.
The result is that if you are using a format that does
rounding (e.g., "%.0f") you should check what your system does.
The following function does traditional rounding;
it might be useful if your awk's printf does unbiased rounding.
# round --- do normal rounding
#
# Arnold Robbins, arnold@gnu.org, August, 1996
# Public Domain
function round(x, ival, aval, fraction)
{
ival = int(x) # integer part, int() truncates
# see if fractional part
if (ival == x) # no fraction
return x
if (x < 0) {
aval = -x # absolute value
ival = int(aval)
fraction = aval - ival
if (fraction >= .5)
return int(x) - 1 # -2.5 --> -3
else
return int(x) # -2.3 --> -2
} else {
fraction = x - ival
if (fraction >= .5)
return ival + 1
else
return ival
}
}
# test harness
{ print $0, round($0) }
One commercial implementation of awk supplies a built-in function,
ord, which takes a character and returns the numeric value for that
character in the machine's character set. If the string passed to
ord has more than one character, only the first one is used.
The inverse of this function is chr (from the function of the same
name in Pascal), which takes a number and returns the corresponding character.
Both functions can be written very nicely in awk; there is no real
reason to build them into the awk interpreter.
# ord.awk --- do ord and chr
#
# Global identifiers:
# _ord_: numerical values indexed by characters
# _ord_init: function to initialize _ord_
#
# Arnold Robbins
# arnold@gnu.org
# Public Domain
# 16 January, 1992
# 20 July, 1992, revised
BEGIN { _ord_init() }
function _ord_init( low, high, i, t)
{
low = sprintf("%c", 7) # BEL is ascii 7
if (low == "\a") { # regular ascii
low = 0
high = 127
} else if (sprintf("%c", 128 + 7) == "\a") {
# ascii, mark parity
low = 128
high = 255
} else { # ebcdic(!)
low = 0
high = 255
}
for (i = low; i <= high; i++) {
t = sprintf("%c", i)
_ord_[t] = i
}
}
Some explanation of the numbers used by chr is worthwhile.
The most prominent character set in use today is ASCII. Although an
eight-bit byte can hold 256 distinct values (from zero to 255), ASCII only
defines characters that use the values from zero to 127.(21)
At least one computer manufacturer that we know of
uses ASCII, but with mark parity, meaning that the leftmost bit in the byte
is always one. What this means is that on those systems, characters
have numeric values from 128 to 255.
Finally, large mainframe systems use the EBCDIC character set, which
uses all 256 values.
While there are other character sets in use on some older systems,
they are not really worth worrying about.
function ord(str, c)
{
# only first character is of interest
c = substr(str, 1, 1)
return _ord_[c]
}
function chr(c)
{
# force c to be numeric by adding 0
return sprintf("%c", c + 0)
}
#### test code ####
# BEGIN \
# {
# for (;;) {
# printf("enter a character: ")
# if (getline var <= 0)
# break
# printf("ord(%s) = %d\n", var, ord(var))
# }
# }
An obvious improvement to these functions would be to move the code for the
_ord_init function into the body of the BEGIN rule. It was
written this way initially for ease of development.
There is a "test program" in a BEGIN rule, for testing the
function. It is commented out for production use.
When doing string processing, it is often useful to be able to join
all the strings in an array into one long string. The following function,
join, accomplishes this task. It is used later in several of
the application programs
(see section Practical awk Programs).
Good function design is important; this function needs to be general, but it
should also have a reasonable default behavior. It is called with an array
and the beginning and ending indices of the elements in the array to be
merged. This assumes that the array indices are numeric--a reasonable
assumption since the array was likely created with split
(see section Szövegmanipuláló beépített függvények).
# join.awk --- join an array into a string
# Arnold Robbins, arnold@gnu.org, Public Domain
# May 1993
function join(array, start, end, sep, result, i)
{
if (sep == "")
sep = " "
else if (sep == SUBSEP) # magic value
sep = ""
result = array[start]
for (i = start + 1; i <= end; i++)
result = result sep array[i]
return result
}
An optional additional argument is the separator to use when joining the
strings back together. If the caller supplies a non-empty value,
join uses it. If it is not supplied, it will have a null
value. In this case, join uses a single blank as a default
separator for the strings. If the value is equal to SUBSEP,
then join joins the strings with no separator between them.
SUBSEP serves as a "magic" value to indicate that there should
be no separation between the component strings.
It would be nice if awk had an assignment operator for concatenation.
The lack of an explicit operator for concatenation makes string operations
more difficult than they really need to be.
The systime function built in to gawk
returns the current time of day as
a timestamp in "seconds since the Epoch." This timestamp
can be converted into a printable date of almost infinitely variable
format using the built-in strftime function.
(For more information on systime and strftime,
see section Dátumfeldolgozó függvények.)
An interesting but difficult problem is to convert a readable representation
of a date back into a timestamp. The ANSI C library provides a mktime
function that does the basic job, converting a canonical representation of a
date into a timestamp.
It would appear at first glance that gawk would have to supply a
mktime built-in function that was simply a "hook" to the C language
version. In fact though, mktime can be implemented entirely in
awk.
Here is a version of mktime for awk. It takes a simple
representation of the date and time, and converts it into a timestamp.
The code is presented here intermixed with explanatory prose. In section Extracting Programs from Texinfo Source Files, you will see how the Texinfo source file for this könyv can be processed to extract the code into a single source file.
The program begins with a descriptive comment and a BEGIN rule
that initializes a table _tm_months. This table is a two-dimensional
array that has the lengths of the months. The first index is zero for
regular years, and one for leap years. The values are the same for all the
months in both kinds of years, except for February; thus the use of multiple
assignment.
# mktime.awk --- convert a canonical date representation
# into a timestamp
# Arnold Robbins, arnold@gnu.org, Public Domain
# May 1993
BEGIN \
{
# Initialize table of month lengths
_tm_months[0,1] = _tm_months[1,1] = 31
_tm_months[0,2] = 28; _tm_months[1,2] = 29
_tm_months[0,3] = _tm_months[1,3] = 31
_tm_months[0,4] = _tm_months[1,4] = 30
_tm_months[0,5] = _tm_months[1,5] = 31
_tm_months[0,6] = _tm_months[1,6] = 30
_tm_months[0,7] = _tm_months[1,7] = 31
_tm_months[0,8] = _tm_months[1,8] = 31
_tm_months[0,9] = _tm_months[1,9] = 30
_tm_months[0,10] = _tm_months[1,10] = 31
_tm_months[0,11] = _tm_months[1,11] = 30
_tm_months[0,12] = _tm_months[1,12] = 31
}
The benefit of merging multiple BEGIN rules
(see section A BEGIN és az END speciális minták)
is particularly clear when writing library files. Functions in library
files can cleanly initialize their own private data and also provide clean-up
actions in private END rules.
The next function is a simple one that computes whether a given year is or is not a leap year. If a year is evenly divisible by four, but not evenly divisible by 100, or if it is evenly divisible by 400, then it is a leap year. Thus, 1904 was a leap year, 1900 was not, but 2000 will be.
# decide if a year is a leap year
function _tm_isleap(year, ret)
{
ret = (year % 4 == 0 && year % 100 != 0) ||
(year % 400 == 0)
return ret
}
This function is only used a few times in this file, and its computation could have been written in-line (at the point where it's used). Making it a separate function made the original development easier, and also avoids the possibility of typing errors when duplicating the code in multiple places.
The next function is more interesting. It does most of the work of
generating a timestamp, which is converting a date and time into some number
of seconds since the Epoch. The caller passes an array (rather
imaginatively named a) containing six
values: the year including century, the month as a number between one and 12,
the day of the month, the hour as a number between zero and 23, the minute in
the hour, and the seconds within the minute.
The function uses several local variables to precompute the number of seconds in an hour, seconds in a day, and seconds in a year. Often, similar C code simply writes out the expression in-line, expecting the compiler to do constant folding. E.g., most C compilers would turn `60 * 60' into `3600' at compile time, instead of recomputing it every time at run time. Precomputing these values makes the function more efficient.
# convert a date into seconds
function _tm_addup(a, total, yearsecs, daysecs,
hoursecs, i, j)
{
hoursecs = 60 * 60
daysecs = 24 * hoursecs
yearsecs = 365 * daysecs
total = (a[1] - 1970) * yearsecs
# extra day for leap years
for (i = 1970; i < a[1]; i++)
if (_tm_isleap(i))
total += daysecs
j = _tm_isleap(a[1])
for (i = 1; i < a[2]; i++)
total += _tm_months[j, i] * daysecs
total += (a[3] - 1) * daysecs
total += a[4] * hoursecs
total += a[5] * 60
total += a[6]
return total
}
The function starts with a first approximation of all the seconds between Midnight, January 1, 1970,(22) and the beginning of the current year. It then goes through all those years, and for every leap year, adds an additional day's worth of seconds.
The variable j holds either one or zero, if the current year is or is not
a leap year.
For every month in the current year prior to the current month, it adds
the number of seconds in the month, using the appropriate entry in the
_tm_months array.
Finally, it adds in the seconds for the number of days prior to the current day, and the number of hours, minutes, and seconds in the current day.
The result is a count of seconds since January 1, 1970. This value is not yet what is needed though. The reason why is described shortly.
The main mktime function takes a single character string argument.
This string is a representation of a date and time in a "canonical"
(fixed) form. This string should be
"year month day hour minute second".
# mktime --- convert a date into seconds,
# compensate for time zone
function mktime(str, res1, res2, a, b, i, j, t, diff)
{
i = split(str, a, " ") # don't rely on FS
if (i != 6)
return -1
# force numeric
for (j in a)
a[j] += 0
# validate
if (a[1] < 1970 ||
a[2] < 1 || a[2] > 12 ||
a[3] < 1 || a[3] > 31 ||
a[4] < 0 || a[4] > 23 ||
a[5] < 0 || a[5] > 59 ||
a[6] < 0 || a[6] > 60 )
return -1
res1 = _tm_addup(a)
t = strftime("%Y %m %d %H %M %S", res1)
if (_tm_debug)
printf("(%s) -> (%s)\n", str, t) > "/dev/stderr"
split(t, b, " ")
res2 = _tm_addup(b)
diff = res1 - res2
if (_tm_debug)
printf("diff = %d seconds\n", diff) > "/dev/stderr"
res1 += diff
return res1
}
The function first splits the string into an array, using spaces and tabs as separators. If there are not six elements in the array, it returns an error, signaled as the value -1. Next, it forces each element of the array to be numeric, by adding zero to it. The following `if' statement then makes sure that each element is within an allowable range. (This checking could be extended further, e.g., to make sure that the day of the month is within the correct range for the particular month supplied.) All of this is essentially preliminary set-up and error checking.
Recall that _tm_addup generated a value in seconds since Midnight,
January 1, 1970. This value is not directly usable as the result we want,
since the calculation does not account for the local timezone. In other
words, the value represents the count in seconds since the Epoch, but only
for UTC (Universal Coordinated Time). If the local timezone is east or west
of UTC, then some number of hours should be either added to, or subtracted from
the resulting timestamp.
For example, 6:23 p.m. in Atlanta, Georgia (USA), is normally five hours west
of (behind) UTC. It is only four hours behind UTC if daylight savings
time is in effect.
If you are calling mktime in Atlanta, with the argument
"1993 5 23 18 23 12", the result from _tm_addup will be
for 6:23 p.m. UTC, which is only 2:23 p.m. in Atlanta. It is necessary to
add another four hours worth of seconds to the result.
How can mktime determine how far away it is from UTC? This is
surprisingly easy. The returned timestamp represents the time passed to
mktime as UTC. This timestamp can be fed back to
strftime, which will format it as a local time; i.e. as
if it already had the UTC difference added in to it. This is done by
giving "%Y %m %d %H %M %S" to strftime as the format
argument. It returns the computed timestamp in the original string
format. The result represents a time that accounts for the UTC
difference. When the new time is converted back to a timestamp, the
difference between the two timestamps is the difference (in seconds)
between the local timezone and UTC. This difference is then added back
to the original result. An example demonstrating this is presented below.
Finally, there is a "main" program for testing the function.
BEGIN {
if (_tm_test) {
printf "Enter date as yyyy mm dd hh mm ss: "
getline _tm_test_date
t = mktime(_tm_test_date)
r = strftime("%Y %m %d %H %M %S", t)
printf "Got back (%s)\n", r
}
}
The entire program uses two variables that can be set on the command
line to control debugging output and to enable the test in the final
BEGIN rule. Here is the result of a test run. (Note that debugging
output is to standard error, and test output is to standard output.)
$ gawk -f mktime.awk -v _tm_test=1 -v _tm_debug=1 -| Enter date as yyyy mm dd hh mm ss: 1993 5 23 15 35 10 error--> (1993 5 23 15 35 10) -> (1993 05 23 11 35 10) error--> diff = 14400 seconds -| Got back (1993 05 23 15 35 10)
The time entered was 3:35 p.m. (15:35 on a 24-hour clock), on May 23, 1993. The first line of debugging output shows the resulting time as UTC--four hours ahead of the local time zone. The second line shows that the difference is 14400 seconds, which is four hours. (The difference is only four hours, since daylight savings time is in effect during May.) The final line of test output shows that the timezone compensation algorithm works; the returned time is the same as the entered time.
This program does not solve the general problem of turning an arbitrary date
representation into a timestamp. That problem is very involved. However,
the mktime function provides a foundation upon which to build. Other
software can convert month names into numeric months, and AM/PM times into
24-hour clocks, to generate the "canonical" format that mktime
requires.
The systime and strftime functions described in
section Dátumfeldolgozó függvények,
provide the minimum functionality necessary for dealing with the time of day
in human readable form. While strftime is extensive, the control
formats are not necessarily easy to remember or intuitively obvious when
reading a program.
The following function, gettimeofday, populates a user-supplied array
with pre-formatted time information. It returns a string with the current
time formatted in the same way as the date utility.
# gettimeofday --- get the time of day in a usable format
# Arnold Robbins, arnold@gnu.org, Public Domain, May 1993
#
# Returns a string in the format of output of date(1)
# Populates the array argument time with individual values:
# time["second"] -- seconds (0 - 59)
# time["minute"] -- minutes (0 - 59)
# time["hour"] -- hours (0 - 23)
# time["althour"] -- hours (0 - 12)
# time["monthday"] -- day of month (1 - 31)
# time["month"] -- month of year (1 - 12)
# time["monthname"] -- name of the month
# time["shortmonth"] -- short name of the month
# time["year"] -- year within century (0 - 99)
# time["fullyear"] -- year with century (19xx or 20xx)
# time["weekday"] -- day of week (Sunday = 0)
# time["altweekday"] -- day of week (Monday = 0)
# time["weeknum"] -- week number, Sunday first day
# time["altweeknum"] -- week number, Monday first day
# time["dayname"] -- name of weekday
# time["shortdayname"] -- short name of weekday
# time["yearday"] -- day of year (0 - 365)
# time["timezone"] -- abbreviation of timezone name
# time["ampm"] -- AM or PM designation
function gettimeofday(time, ret, now, i)
{
# get time once, avoids unnecessary system calls
now = systime()
# return date(1)-style output
ret = strftime("%a %b %d %H:%M:%S %Z %Y", now)
# clear out target array
for (i in time)
delete time[i]
# fill in values, force numeric values to be
# numeric by adding 0
time["second"] = strftime("%S", now) + 0
time["minute"] = strftime("%M", now) + 0
time["hour"] = strftime("%H", now) + 0
time["althour"] = strftime("%I", now) + 0
time["monthday"] = strftime("%d", now) + 0
time["month"] = strftime("%m", now) + 0
time["monthname"] = strftime("%B", now)
time["shortmonth"] = strftime("%b", now)
time["year"] = strftime("%y", now) + 0
time["fullyear"] = strftime("%Y", now) + 0
time["weekday"] = strftime("%w", now) + 0
time["altweekday"] = strftime("%u", now) + 0
time["dayname"] = strftime("%A", now)
time["shortdayname"] = strftime("%a", now)
time["yearday"] = strftime("%j", now) + 0
time["timezone"] = strftime("%Z", now)
time["ampm"] = strftime("%p", now)
time["weeknum"] = strftime("%U", now) + 0
time["altweeknum"] = strftime("%W", now) + 0
return ret
}
The string indices are easier to use and read than the various formats
required by strftime. The alarm program presented in
section An Alarm Clock Program,
uses this function.
The gettimeofday function is presented above as it was written. A
more general design for this function would have allowed the user to supply
an optional timestamp value that would have been used instead of the current
time.
The BEGIN and END rules are each executed exactly once, at
the beginning and end respectively of your awk program
(see section A BEGIN és az END speciális minták).
We (the gawk authors) once had a user who mistakenly thought that the
BEGIN rule was executed at the beginning of each data file and the
END rule was executed at the end of each data file. When informed
that this was not the case, the user requested that we add new special
patterns to gawk, named BEGIN_FILE and END_FILE, that
would have the desired behavior. He even supplied us the code to do so.
However, after a little thought, I came up with the following library program.
It arranges to call two user-supplied functions, beginfile and
endfile, at the beginning and end of each data file.
Besides solving the problem in only nine(!) lines of code, it does so
portably; this will work with any implementation of awk.
# transfile.awk
#
# Give the user a hook for filename transitions
#
# The user must supply functions beginfile() and endfile()
# that each take the name of the file being started or
# finished, respectively.
#
# Arnold Robbins, arnold@gnu.org, January 1992
# Public Domain
FILENAME != _oldfilename \
{
if (_oldfilename != "")
endfile(_oldfilename)
_oldfilename = FILENAME
beginfile(FILENAME)
}
END { endfile(FILENAME) }
This file must be loaded before the user's "main" program, so that the rule it supplies will be executed first.
This rule relies on awk's FILENAME variable that
automatically changes for each new data file. The current file name is
saved in a private variable, _oldfilename. If FILENAME does
not equal _oldfilename, then a new data file is being processed, and
it is necessary to call endfile for the old file. Since
endfile should only be called if a file has been processed, the
program first checks to make sure that _oldfilename is not the null
string. The program then assigns the current file name to
_oldfilename, and calls beginfile for the file.
Since, like all awk variables, _oldfilename will be
initialized to the null string, this rule executes correctly even for the
first data file.
The program also supplies an END rule, to do the final processing for
the last file. Since this END rule comes before any END rules
supplied in the "main" program, endfile will be called first. Once
again the value of multiple BEGIN and END rules should be clear.
This version has same problem as the first version of nextfile
(see section Implementing nextfile as a Function).
If the same data file occurs twice in a row on command line, then
endfile and beginfile will not be executed at the end of the
first pass and at the beginning of the second pass.
This version solves the problem.
# ftrans.awk --- handle data file transitions
#
# user supplies beginfile() and endfile() functions
#
# Arnold Robbins, arnold@gnu.org, November 1992
# Public Domain
FNR == 1 {
if (_filename_ != "")
endfile(_filename_)
_filename_ = FILENAME
beginfile(FILENAME)
}
END { endfile(_filename_) }
In section Counting Things, you will see how this library function can be used, and how it simplifies writing the main program.
Most utilities on POSIX compatible systems take options or "switches" on
the command line that can be used to change the way a program behaves.
awk is an example of such a program
(see section Command Line Options).
Often, options take arguments, data that the program needs to
correctly obey the command line option. For example, awk's
`-F' option requires a string to use as the field separator.
The first occurrence on the command line of either `--' or a
string that does not begin with `-' ends the options.
Most Unix systems provide a C function named getopt for processing
command line arguments. The programmer provides a string describing the one
letter options. If an option requires an argument, it is followed in the
string with a colon. getopt is also passed the
count and values of the command line arguments, and is called in a loop.
getopt processes the command line arguments for option letters.
Each time around the loop, it returns a single character representing the
next option letter that it found, or `?' if it found an invalid option.
When it returns -1, there are no options left on the command line.
When using getopt, options that do not take arguments can be
grouped together. Furthermore, options that take arguments require that the
argument be present. The argument can immediately follow the option letter,
or it can be a separate command line argument.
Given a hypothetical program that takes three command line options, `-a', `-b', and `-c', and `-b' requires an argument, all of the following are valid ways of invoking the program:
prog -a -b foo -c data1 data2 data3 prog -ac -bfoo -- data1 data2 data3 prog -acbfoo data1 data2 data3
Notice that when the argument is grouped with its option, the rest of the command line argument is considered to be the option's argument. In the above example, `-acbfoo' indicates that all of the `-a', `-b', and `-c' options were supplied, and that `foo' is the argument to the `-b' option.
getopt provides four external variables that the programmer can use.
optind
argv) where the first
non-option command line argument can be found.
optarg
opterr
getopt prints an error message when it finds an invalid
option. Setting opterr to zero disables this feature. (An
application might wish to print its own error message.)
optopt
The following C fragment shows how getopt might process command line
arguments for awk.
int
main(int argc, char *argv[])
{
...
/* print our own message */
opterr = 0;
while ((c = getopt(argc, argv, "v:f:F:W:")) != -1) {
switch (c) {
case 'f': /* file */
...
break;
case 'F': /* field separator */
...
break;
case 'v': /* variable assignment */
...
break;
case 'W': /* extension */
...
break;
case '?':
default:
usage();
break;
}
}
...
}
As a side point, gawk actually uses the GNU getopt_long
function to process both normal and GNU-style long options
(see section Command Line Options).
The abstraction provided by getopt is very useful, and would be quite
handy in awk programs as well. Here is an awk version of
getopt. This function highlights one of the greatest weaknesses in
awk, which is that it is very poor at manipulating single characters.
Repeated calls to substr are necessary for accessing individual
characters (see section Szövegmanipuláló beépített függvények).
The discussion walks through the code a bit at a time.
# getopt --- do C library getopt(3) function in awk # # arnold@gnu.org # Public domain # # Initial version: March, 1991 # Revised: May, 1993 # External variables: # Optind -- index of ARGV for first non-option argument # Optarg -- string value of argument to current option # Opterr -- if non-zero, print our own diagnostic # Optopt -- current option letter # Returns # -1 at end of options # ? for unrecognized option # <c> a character representing the current option # Private Data # _opti index in multi-flag option, e.g., -abc
The function starts out with some documentation: who wrote the code, and when it was revised, followed by a list of the global variables it uses, what the return values are and what they mean, and any global variables that are "private" to this library function. Such documentation is essential for any program, and particularly for library functions.
function getopt(argc, argv, options, optl, thisopt, i)
{
optl = length(options)
if (optl == 0) # no options given
return -1
if (argv[Optind] == "--") { # all done
Optind++
_opti = 0
return -1
} else if (argv[Optind] !~ /^-[^: \t\n\f\r\v\b]/) {
_opti = 0
return -1
}
The function first checks that it was indeed called with a string of options
(the options parameter). If options has a zero length,
getopt immediately returns -1.
The next thing to check for is the end of the options. A `--' ends the
command line options, as does any command line argument that does not begin
with a `-'. Optind is used to step through the array of command
line arguments; it retains its value across calls to getopt, since it
is a global variable.
The regexp used, /^-[^: \t\n\f\r\v\b]/, is
perhaps a bit of overkill; it checks for a `-' followed by anything
that is not whitespace and not a colon.
If the current command line argument does not match this pattern,
it is not an option, and it ends option processing.
if (_opti == 0)
_opti = 2
thisopt = substr(argv[Optind], _opti, 1)
Optopt = thisopt
i = index(options, thisopt)
if (i == 0) {
if (Opterr)
printf("%c -- invalid option\n",
thisopt) > "/dev/stderr"
if (_opti >= length(argv[Optind])) {
Optind++
_opti = 0
} else
_opti++
return "?"
}
The _opti variable tracks the position in the current command line
argument (argv[Optind]). In the case that multiple options were
grouped together with one `-' (e.g., `-abx'), it is necessary
to return them to the user one at a time.
If _opti is equal to zero, it is set to two, the index in the string
of the next character to look at (we skip the `-', which is at position
one). The variable thisopt holds the character, obtained with
substr. It is saved in Optopt for the main program to use.
If thisopt is not in the options string, then it is an
invalid option. If Opterr is non-zero, getopt prints an error
message on the standard error that is similar to the message from the C
version of getopt.
Since the option is invalid, it is necessary to skip it and move on to the
next option character. If _opti is greater than or equal to the
length of the current command line argument, then it is necessary to move on
to the next one, so Optind is incremented and _opti is reset
to zero. Otherwise, Optind is left alone and _opti is merely
incremented.
In any case, since the option was invalid, getopt returns `?'.
The main program can examine Optopt if it needs to know what the
invalid option letter actually was.
if (substr(options, i + 1, 1) == ":") {
# get option argument
if (length(substr(argv[Optind], _opti + 1)) > 0)
Optarg = substr(argv[Optind], _opti + 1)
else
Optarg = argv[++Optind]
_opti = 0
} else
Optarg = ""
If the option requires an argument, the option letter is followed by a colon
in the options string. If there are remaining characters in the
current command line argument (argv[Optind]), then the rest of that
string is assigned to Optarg. Otherwise, the next command line
argument is used (`-xFOO' vs. `-x FOO'). In either case,
_opti is reset to zero, since there are no more characters left to
examine in the current command line argument.
if (_opti == 0 || _opti >= length(argv[Optind])) {
Optind++
_opti = 0
} else
_opti++
return thisopt
}
Finally, if _opti is either zero or greater than the length of the
current command line argument, it means this element in argv is
through being processed, so Optind is incremented to point to the
next element in argv. If neither condition is true, then only
_opti is incremented, so that the next option letter can be processed
on the next call to getopt.
BEGIN {
Opterr = 1 # default is to diagnose
Optind = 1 # skip ARGV[0]
# test program
if (_getopt_test) {
while ((_go_c = getopt(ARGC, ARGV, "ab:cd")) != -1)
printf("c = <%c>, optarg = <%s>\n",
_go_c, Optarg)
printf("non-option arguments:\n")
for (; Optind < ARGC; Optind++)
printf("\tARGV[%d] = <%s>\n",
Optind, ARGV[Optind])
}
}
The BEGIN rule initializes both Opterr and Optind to one.
Opterr is set to one, since the default behavior is for getopt
to print a diagnostic message upon seeing an invalid option. Optind
is set to one, since there's no reason to look at the program name, which is
in ARGV[0].
The rest of the BEGIN rule is a simple test program. Here is the
result of two sample runs of the test program.
$ awk -f getopt.awk -v _getopt_test=1 -- -a -cbARG bax -x -| c = <a>, optarg = <> -| c = <c>, optarg = <> -| c = <b>, optarg = <ARG> -| non-option arguments: -| ARGV[3] = <bax> -| ARGV[4] = <-x> $ awk -f getopt.awk -v _getopt_test=1 -- -a -x -- xyz abc -| c = <a>, optarg = <> error--> x -- invalid option -| c = <?>, optarg = <> -| non-option arguments: -| ARGV[4] = <xyz> -| ARGV[5] = <abc>
The first `--' terminates the arguments to awk, so that it does
not try to interpret the `-a' etc. as its own options.
Several of the sample programs presented in
section Practical awk Programs,
use getopt to process their arguments.
The `/dev/user' special file
(see section Speciális file nevek gawk-ban)
provides access to the current user's real and effective user and group id
numbers, and if available, the user's supplementary group set.
However, since these are numbers, they do not provide very useful
information to the average user. There needs to be some way to find the
user information associated with the user and group numbers. This
section presents a suite of functions for retrieving information from the
user database. See section Reading the Group Database,
for a similar suite that retrieves information from the group database.
The POSIX standard does not define the file where user information is
kept. Instead, it provides the <pwd.h> header file
and several C language subroutines for obtaining user information.
The primary function is getpwent, for "get password entry."
The "password" comes from the original user database file,
`/etc/passwd', which kept user information, along with the
encrypted passwords (hence the name).
While an awk program could simply read `/etc/passwd' directly
(the format is well known), because of the way password
files are handled on networked systems,
this file may not contain complete information about the system's set of users.
To be sure of being
able to produce a readable, complete version of the user database, it is
necessary to write a small C program that calls getpwent.
getpwent is defined to return a pointer to a struct passwd.
Each time it is called, it returns the next entry in the database.
When there are no more entries, it returns NULL, the null pointer.
When this happens, the C program should call endpwent to close the
database.
Here is pwcat, a C program that "cats" the password database.
/*
* pwcat.c
*
* Generate a printable version of the password database
*
* Arnold Robbins
* arnold@gnu.org
* May 1993
* Public Domain
*/
#include <stdio.h>
#include <pwd.h>
int
main(argc, argv)
int argc;
char **argv;
{
struct passwd *p;
while ((p = getpwent()) != NULL)
printf("%s:%s:%d:%d:%s:%s:%s\n",
p->pw_name, p->pw_passwd, p->pw_uid,
p->pw_gid, p->pw_gecos, p->pw_dir, p->pw_shell);
endpwent();
exit(0);
}
If you don't understand C, don't worry about it.
The output from pwcat is the user database, in the traditional
`/etc/passwd' format of colon-separated fields. The fields are:
$HOME).
Here are a few lines representative of pwcat's output.
$ pwcat -| root:3Ov02d5VaUPB6:0:1:Operator:/:/bin/sh -| nobody:*:65534:65534::/: -| daemon:*:1:1::/: -| sys:*:2:2::/:/bin/csh -| bin:*:3:3::/bin: -| arnold:xyzzy:2076:10:Arnold Robbins:/home/arnold:/bin/sh -| miriam:yxaay:112:10:Miriam Robbins:/home/miriam:/bin/sh -| andy:abcca2:113:10:Andy Jacobs:/home/andy:/bin/sh ...
With that introduction, here is a group of functions for getting user information. There are several functions here, corresponding to the C functions of the same name.
# passwd.awk --- access password file information
# Arnold Robbins, arnold@gnu.org, Public Domain
# May 1993
BEGIN {
# tailor this to suit your system
_pw_awklib = "/usr/local/libexec/awk/"
}
function _pw_init( oldfs, oldrs, olddol0, pwcat)
{
if (_pw_inited)
return
oldfs = FS
oldrs = RS
olddol0 = $0
FS = ":"
RS = "\n"
pwcat = _pw_awklib "pwcat"
while ((pwcat | getline) > 0) {
_pw_byname[$1] = $0
_pw_byuid[$3] = $0
_pw_bycount[++_pw_total] = $0
}
close(pwcat)
_pw_count = 0
_pw_inited = 1
FS = oldfs
RS = oldrs
$0 = olddol0
}
The BEGIN rule sets a private variable to the directory where
pwcat is stored. Since it is used to help out an awk library
routine, we have chosen to put it in `/usr/local/libexec/awk'.
You might want it to be in a different directory on your system.
The function _pw_init keeps three copies of the user information
in three associative arrays. The arrays are indexed by user name
(_pw_byname), by user-id number (_pw_byuid), and by order of
occurrence (_pw_bycount).
The variable _pw_inited is used for efficiency; _pw_init only
needs to be called once.
Since this function uses getline to read information from
pwcat, it first saves the values of FS, RS, and
$0. Doing so is necessary, since these functions could be called
from anywhere within a user's program, and the user may have his or her
own values for FS and RS.
The main part of the function uses a loop to read database lines, split
the line into fields, and then store the line into each array as necessary.
When the loop is done, _pw_init cleans up by closing the pipeline,
setting _pw_inited to one, and restoring FS, RS, and
$0. The use of _pw_count will be explained below.
function getpwnam(name)
{
_pw_init()
if (name in _pw_byname)
return _pw_byname[name]
return ""
}
The getpwnam function takes a user name as a string argument. If that
user is in the database, it returns the appropriate line. Otherwise it
returns the null string.
function getpwuid(uid)
{
_pw_init()
if (uid in _pw_byuid)
return _pw_byuid[uid]
return ""
}
Similarly,
the getpwuid function takes a user-id number argument. If that
user number is in the database, it returns the appropriate line. Otherwise it
returns the null string.
function getpwent()
{
_pw_init()
if (_pw_count < _pw_total)
return _pw_bycount[++_pw_count]
return ""
}
The getpwent function simply steps through the database, one entry at
a time. It uses _pw_count to track its current position in the
_pw_bycount array.
function endpwent()
{
_pw_count = 0
}
The endpwent function resets _pw_count to zero, so that
subsequent calls to getpwent will start over again.
A conscious design decision in this suite is that each subroutine calls
_pw_init to initialize the database arrays. The overhead of running
a separate process to generate the user database, and the I/O to scan it,
will only be incurred if the user's main program actually calls one of these
functions. If this library file is loaded along with a user's program, but
none of the routines are ever called, then there is no extra run-time overhead.
(The alternative would be to move the body of _pw_init into a
BEGIN rule, which would always run pwcat. This simplifies the
code but runs an extra process that may never be needed.)
In turn, calling _pw_init is not too expensive, since the
_pw_inited variable keeps the program from reading the data more than
once. If you are worried about squeezing every last cycle out of your
awk program, the check of _pw_inited could be moved out of
_pw_init and duplicated in all the other functions. In practice,
this is not necessary, since most awk programs are I/O bound, and it
would clutter up the code.
The id program in section Printing Out User Information,
uses these functions.
Much of the discussion presented in
section Reading the User Database,
applies to the group database as well. Although there has traditionally
been a well known file, `/etc/group', in a well known format, the POSIX
standard only provides a set of C library routines
(<grp.h> and getgrent)
for accessing the information.
Even though this file may exist, it likely does not have
complete information. Therefore, as with the user database, it is necessary
to have a small C program that generates the group database as its output.
Here is grcat, a C program that "cats" the group database.
/*
* grcat.c
*
* Generate a printable version of the group database
*
* Arnold Robbins, arnold@gnu.org
* May 1993
* Public Domain
*/
#include <stdio.h>
#include <grp.h>
int
main(argc, argv)
int argc;
char **argv;
{
struct group *g;
int i;
while ((g = getgrent()) != NULL) {
printf("%s:%s:%d:", g->gr_name, g->gr_passwd,
g->gr_gid);
for (i = 0; g->gr_mem[i] != NULL; i++) {
printf("%s", g->gr_mem[i]);
if (g->gr_mem[i+1] != NULL)
putchar(',');
}
putchar('\n');
}
endgrent();
exit(0);
}
Each line in the group database represent one group. The fields are separated with colons, and represent the following information.
$5 through $NF.
(Note that `/dev/user' is a gawk extension;
see section Speciális file nevek gawk-ban.)
Here is what running grcat might produce:
$ grcat -| wheel:*:0:arnold -| nogroup:*:65534: -| daemon:*:1: -| kmem:*:2: -| staff:*:10:arnold,miriam,andy -| other:*:20: ...
Here are the functions for obtaining information from the group database. There are several, modeled after the C library functions of the same names.
# group.awk --- functions for dealing with the group file
# Arnold Robbins, arnold@gnu.org, Public Domain
# May 1993
BEGIN \
{
# Change to suit your system
_gr_awklib = "/usr/local/libexec/awk/"
}
function _gr_init( oldfs, oldrs, olddol0, grcat, n, a, i)
{
if (_gr_inited)
return
oldfs = FS
oldrs = RS
olddol0 = $0
FS = ":"
RS = "\n"
grcat = _gr_awklib "grcat"
while ((grcat | getline) > 0) {
if ($1 in _gr_byname)
_gr_byname[$1] = _gr_byname[$1] "," $4
else
_gr_byname[$1] = $0
if ($3 in _gr_bygid)
_gr_bygid[$3] = _gr_bygid[$3] "," $4
else
_gr_bygid[$3] = $0
n = split($4, a, "[ \t]*,[ \t]*")
for (i = 1; i <= n; i++)
if (a[i] in _gr_groupsbyuser)
_gr_groupsbyuser[a[i]] = \
_gr_groupsbyuser[a[i]] " " $1
else
_gr_groupsbyuser[a[i]] = $1
_gr_bycount[++_gr_count] = $0
}
close(grcat)
_gr_count = 0
_gr_inited++
FS = oldfs
RS = oldrs
$0 = olddol0
}
The BEGIN rule sets a private variable to the directory where
grcat is stored. Since it is used to help out an awk library
routine, we have chosen to put it in `/usr/local/libexec/awk'. You might
want it to be in a different directory on your system.
These routines follow the same general outline as the user database routines
(see section Reading the User Database).
The _gr_inited variable is used to
ensure that the database is scanned no more than once.
The _gr_init function first saves FS, RS, and
$0, and then sets FS and RS to the correct values for
scanning the group information.
The group information is stored is several associative arrays.
The arrays are indexed by group name (_gr_byname), by group-id number
(_gr_bygid), and by position in the database (_gr_bycount).
There is an additional array indexed by user name (_gr_groupsbyuser),
that is a space separated list of groups that each user belongs to.
Unlike the user database, it is possible to have multiple records in the database for the same group. This is common when a group has a large number of members. Such a pair of entries might look like:
tvpeople:*:101:johny,jay,arsenio tvpeople:*:101:david,conan,tom,joan
For this reason, _gr_init looks to see if a group name or
group-id number has already been seen. If it has, then the user names are
simply concatenated onto the previous list of users. (There is actually a
subtle problem with the code presented above. Suppose that
the first time there were no names. This code adds the names with
a leading comma. It also doesn't check that there is a $4.)
Finally, _gr_init closes the pipeline to grcat, restores
FS, RS, and $0, initializes _gr_count to zero
(it is used later), and makes _gr_inited non-zero.
function getgrnam(group)
{
_gr_init()
if (group in _gr_byname)
return _gr_byname[group]
return ""
}
The getgrnam function takes a group name as its argument, and if that
group exists, it is returned. Otherwise, getgrnam returns the null
string.
function getgrgid(gid)
{
_gr_init()
if (gid in _gr_bygid)
return _gr_bygid[gid]
return ""
}
The getgrgid function is similar, it takes a numeric group-id, and
looks up the information associated with that group-id.
function getgruser(user)
{
_gr_init()
if (user in _gr_groupsbyuser)
return _gr_groupsbyuser[user]
return ""
}
The getgruser function does not have a C counterpart. It takes a
user name, and returns the list of groups that have the user as a member.
function getgrent()
{
_gr_init()
if (++_gr_count in _gr_bycount)
return _gr_bycount[_gr_count]
return ""
}
The getgrent function steps through the database one entry at a time.
It uses _gr_count to track its position in the list.
function endgrent()
{
_gr_count = 0
}
endgrent resets _gr_count to zero so that getgrent can
start over again.
As with the user database routines, each function calls _gr_init to
initialize the arrays. Doing so only incurs the extra overhead of running
grcat if these functions are used (as opposed to moving the body of
_gr_init into a BEGIN rule).
Most of the work is in scanning the database and building the various
associative arrays. The functions that the user calls are themselves very
simple, relying on awk's associative arrays to do work.
The id program in section Printing Out User Information,
uses these functions.
Due to the way the awk language evolved, variables are either
global (usable by the entire program), or local (usable just by
a specific function). There is no intermediate state analogous to
static variables in C.
Library functions often need to have global variables that they can use to
preserve state information between calls to the function. For example,
getopt's variable _opti
(see section Processing Command Line Options),
and the _tm_months array used by mktime
(see section Turning Dates Into Timestamps).
Such variables are called private, since the only functions that need to
use them are the ones in the library.
When writing a library function, you should try to choose names for your private variables so that they will not conflict with any variables used by either another library function or a user's main program. For example, a name like `i' or `j' is not a good choice, since user programs often use variable names like these for their own purposes.
The example programs shown in this chapter all start the names of their private variables with an underscore (`_'). Users generally don't use leading underscores in their variable names, so this convention immediately decreases the chances that the variable name will be accidentally shared with the user's program.
In addition, several of the library functions use a prefix that helps
indicate what function or set of functions uses the variables. For example,
_tm_months in mktime
(see section Turning Dates Into Timestamps), and
_pw_byname in the user data base routines
(see section Reading the User Database).
This convention is recommended, since it even further decreases the chance
of inadvertent conflict among variable names.
Note that this convention can be used equally well both for variable names
and for private function names too.
While I could have re-written all the library routines to use this
convention, I did not do so, in order to show how my own awk
programming style has evolved, and to provide some basis for this
discussion.
As a final note on variable naming, if a function makes global variables
available for use by a main program, it is a good convention to start that
variable's name with a capital letter.
For example, getopt's Opterr and Optind variables
(see section Processing Command Line Options).
The leading capital letter indicates that it is global, while the fact that
the variable name is not all capital letters indicates that the variable is
not one of awk's built-in variables, like FS.
It is also important that all variables in library functions that do not need to save state are in fact declared local. If this is not done, the variable could accidentally be used in the user's program, leading to bugs that are very difficult to track down.
function lib_func(x, y, l1, l2)
{
...
use variable some_var # some_var could be local
... # but is not by oversight
}
A different convention, common in the Tcl community, is to use a single
associative array to hold the values needed by the library function(s), or
"package." This significantly decreases the number of actual global names
in use. For example, the functions described in
section Reading the User Database,
might have used PW_data["inited"], PW_data["total"],
PW_data["count"] and PW_data["awklib"], instead of
_pw_inited, _pw_awklib, _pw_total,
and _pw_count.
The conventions presented in this section are exactly that, conventions. You are not required to write your programs this way, we merely recommend that you do so.
awk Programs
This chapter presents a potpourri of awk programs for your reading
enjoyment.
There are two sections. The first presents awk
versions of several common POSIX utilities.
The second is a grab-bag of interesting programs.
Many of these programs use the library functions presented in
section A Library of awk Functions.
This section presents a number of POSIX utilities that are implemented in
awk. Re-inventing these programs in awk is often enjoyable,
since the algorithms can be very clearly expressed, and usually the code is
very concise and simple. This is true because awk does so much for you.
It should be noted that these programs are not necessarily intended to
replace the installed versions on your system. Instead, their
purpose is to illustrate awk language programming for "real world"
tasks.
The programs are presented in alphabetical order.
The cut utility selects, or "cuts," either characters or fields
from its standard
input and sends them to its standard output. cut can cut out either
a list of characters, or a list of fields. By default, fields are separated
by tabs, but you may supply a command line option to change the field
delimiter, i.e. the field separator character. cut's definition
of fields is less general than awk's.
A common use of cut might be to pull out just the login name of
logged-on users from the output of who. For example, the following
pipeline generates a sorted, unique list of the logged on users:
who | cut -c1-8 | sort | uniq
The options for cut are:
-c list
-f list
-d delim
-s
The awk implementation of cut uses the getopt library
function (see section Processing Command Line Options),
and the join library function
(see section Merging an Array Into a String).
The program begins with a comment describing the options and a usage
function which prints out a usage message and exits. usage is called
if invalid arguments are supplied.
# cut.awk --- implement cut in awk
# Arnold Robbins, arnold@gnu.org, Public Domain
# May 1993
# Options:
# -f list Cut fields
# -d c Field delimiter character
# -c list Cut characters
#
# -s Suppress lines without the delimiter character
function usage( e1, e2)
{
e1 = "usage: cut [-f list] [-d c] [-s] [files...]"
e2 = "usage: cut [-c list] [files...]"
print e1 > "/dev/stderr"
print e2 > "/dev/stderr"
exit 1
}
The variables e1 and e2 are used so that the function
fits nicely on the
page.
Next comes a BEGIN rule that parses the command line options.
It sets FS to a single tab character, since that is cut's
default field separator. The output field separator is also set to be the
same as the input field separator. Then getopt is used to step
through the command line options. One or the other of the variables
by_fields or by_chars is set to true, to indicate that
processing should be done by fields or by characters respectively.
When cutting by characters, the output field separator is set to the null
string.
BEGIN \
{
FS = "\t" # default
OFS = FS
while ((c = getopt(ARGC, ARGV, "sf:c:d:")) != -1) {
if (c == "f") {
by_fields = 1
fieldlist = Optarg
} else if (c == "c") {
by_chars = 1
fieldlist = Optarg
OFS = ""
} else if (c == "d") {
if (length(Optarg) > 1) {
printf("Using first character of %s" \
" for delimiter\n", Optarg) > "/dev/stderr"
Optarg = substr(Optarg, 1, 1)
}
FS = Optarg
OFS = FS
if (FS == " ") # defeat awk semantics
FS = "[ ]"
} else if (c == "s")
suppress++
else
usage()
}
for (i = 1; i < Optind; i++)
ARGV[i] = ""
Special care is taken when the field delimiter is a space. Using
" " (a single space) for the value of FS is
incorrect---awk would
separate fields with runs of spaces, tabs and/or newlines, and we want them to be
separated with individual spaces. Also, note that after getopt is
through, we have to clear out all the elements of ARGV from one to
Optind, so that awk will not try to process the command line
options as file names.
After dealing with the command line options, the program verifies that the
options make sense. Only one or the other of `-c' and `-f' should
be used, and both require a field list. Then either set_fieldlist or
set_charlist is called to pull apart the list of fields or
characters.
if (by_fields && by_chars)
usage()
if (by_fields == 0 && by_chars == 0)
by_fields = 1 # default
if (fieldlist == "") {
print "cut: needs list for -c or -f" > "/dev/stderr"
exit 1
}
if (by_fields)
set_fieldlist()
else
set_charlist()
}
Here is set_fieldlist. It first splits the field list apart
at the commas, into an array. Then, for each element of the array, it
looks to see if it is actually a range, and if so splits it apart. The range
is verified to make sure the first number is smaller than the second.
Each number in the list is added to the flist array, which simply
lists the fields that will be printed.
Normal field splitting is used.
The program lets awk
handle the job of doing the field splitting.
function set_fieldlist( n, m, i, j, k, f, g)
{
n = split(fieldlist, f, ",")
j = 1 # index in flist
for (i = 1; i <= n; i++) {
if (index(f[i], "-") != 0) { # a range
m = split(f[i], g, "-")
if (m != 2 || g[1] >= g[2]) {
printf("bad field list: %s\n",
f[i]) > "/dev/stderr"
exit 1
}
for (k = g[1]; k <= g[2]; k++)
flist[j++] = k
} else
flist[j++] = f[i]
}
nfields = j - 1
}
The set_charlist function is more complicated than set_fieldlist.
The idea here is to use gawk's FIELDWIDTHS variable
(see section Meghatározott szélességű adatok beolvasása),
which describes constant width input. When using a character list, that is
exactly what we have.
Setting up FIELDWIDTHS is more complicated than simply listing the
fields that need to be printed. We have to keep track of the fields to be
printed, and also the intervening characters that have to be skipped.
For example, suppose you wanted characters one through eight, 15, and
22 through 35. You would use `-c 1-8,15,22-35'. The necessary value
for FIELDWIDTHS would be "8 6 1 6 14". This gives us five
fields, and what should be printed are $1, $3, and $5.
The intermediate fields are "filler," stuff in between the desired data.
flist lists the fields to be printed, and t tracks the
complete field list, including filler fields.
function set_charlist( field, i, j, f, g, t,
filler, last, len)
{
field = 1 # count total fields
n = split(fieldlist, f, ",")
j = 1 # index in flist
for (i = 1; i <= n; i++) {
if (index(f[i], "-") != 0) { # range
m = split(f[i], g, "-")
if (m != 2 || g[1] >= g[2]) {
printf("bad character list: %s\n",
f[i]) > "/dev/stderr"
exit 1
}
len = g[2] - g[1] + 1
if (g[1] > 1) # compute length of filler
filler = g[1] - last - 1
else
filler = 0
if (filler)
t[field++] = filler
t[field++] = len # length of field
last = g[2]
flist[j++] = field - 1
} else {
if (f[i] > 1)
filler = f[i] - last - 1
else
filler = 0
if (filler)
t[field++] = filler
t[field++] = 1
last = f[i]
flist[j++] = field - 1
}
}
FIELDWIDTHS = join(t, 1, field - 1)
nfields = j - 1
}
Here is the rule that actually processes the data. If the `-s' option
was given, then suppress will be true. The first if statement
makes sure that the input record does have the field separator. If
cut is processing fields, suppress is true, and the field
separator character is not in the record, then the record is skipped.
If the record is valid, then at this point, gawk has split the data
into fields, either using the character in FS or using fixed-length
fields and FIELDWIDTHS. The loop goes through the list of fields
that should be printed. If the corresponding field has data in it, it is
printed. If the next field also has data, then the separator character is
written out in between the fields.
{
if (by_fields && suppress && $0 !~ FS)
next
for (i = 1; i <= nfields; i++) {
if ($flist[i] != "") {
printf "%s", $flist[i]
if (i < nfields && $flist[i+1] != "")
printf "%s", OFS
}
}
print ""
}
This version of cut relies on gawk's FIELDWIDTHS
variable to do the character-based cutting. While it would be possible in
other awk implementations to use substr
(see section Szövegmanipuláló beépített függvények),
it would also be extremely painful to do so.
The FIELDWIDTHS variable supplies an elegant solution to the problem
of picking the input line apart by characters.
The egrep utility searches files for patterns. It uses regular
expressions that are almost identical to those available in awk
(see section Reguláris kifejezés konstansok). It is used this way:
egrep [ options ] 'pattern' files ...
The pattern is a regexp.
In typical usage, the regexp is quoted to prevent the shell from expanding
any of the special characters as file name wildcards.
Normally, egrep prints the
lines that matched. If multiple file names are provided on the command
line, each output line is preceded by the name of the file and a colon.
The options are:
-c
-s
-v
egrep prints the lines that do
not match the pattern, and exits successfully if the pattern was not
matched.
-i
-l
-e pattern
This version uses the getopt library function
(see section Processing Command Line Options),
and the file transition library program
(see section Noting Data File Boundaries).
The program begins with a descriptive comment, and then a BEGIN rule
that processes the command line arguments with getopt. The `-i'
(ignore case) option is particularly easy with gawk; we just use the
IGNORECASE built in variable
(see section Beépített változók).
# egrep.awk --- simulate egrep in awk
# Arnold Robbins, arnold@gnu.org, Public Domain
# May 1993
# Options:
# -c count of lines
# -s silent - use exit value
# -v invert test, success if no match
# -i ignore case
# -l print filenames only
# -e argument is pattern
BEGIN {
while ((c = getopt(ARGC, ARGV, "ce:svil")) != -1) {
if (c == "c")
count_only++
else if (c == "s")
no_print++
else if (c == "v")
invert++
else if (c == "i")
IGNORECASE = 1
else if (c == "l")
filenames_only++
else if (c == "e")
pattern = Optarg
else
usage()
}
Next comes the code that handles the egrep specific behavior. If no
pattern was supplied with `-e', the first non-option on the command
line is used. The awk command line arguments up to ARGV[Optind]
are cleared, so that awk won't try to process them as files. If no
files were specified, the standard input is used, and if multiple files were
specified, we make sure to note this so that the file names can precede the
matched lines in the output.
The last two lines are commented out, since they are not needed in
gawk. They should be uncommented if you have to use another version
of awk.
if (pattern == "")
pattern = ARGV[Optind++]
for (i = 1; i < Optind; i++)
ARGV[i] = ""
if (Optind >= ARGC) {
ARGV[1] = "-"
ARGC = 2
} else if (ARGC - Optind > 1)
do_filenames++
# if (IGNORECASE)
# pattern = tolower(pattern)
}
The next set of lines should be uncommented if you are not using
gawk. This rule translates all the characters in the input line
into lower-case if the `-i' option was specified. The rule is
commented out since it is not necessary with gawk.
#{
# if (IGNORECASE)
# $0 = tolower($0)
#}
The beginfile function is called by the rule in `ftrans.awk'
when each new file is processed. In this case, it is very simple; all it
does is initialize a variable fcount to zero. fcount tracks
how many lines in the current file matched the pattern.
function beginfile(junk)
{
fcount = 0
}
The endfile function is called after each file has been processed.
It is used only when the user wants a count of the number of lines that
matched. no_print will be true only if the exit status is desired.
count_only will be true if line counts are desired. egrep
will therefore only print line counts if printing and counting are enabled.
The output format must be adjusted depending upon the number of files to be
processed. Finally, fcount is added to total, so that we
know how many lines altogether matched the pattern.
function endfile(file)
{
if (! no_print && count_only)
if (do_filenames)
print file ":" fcount
else
print fcount
total += fcount
}
This rule does most of the work of matching lines. The variable
matches will be true if the line matched the pattern. If the user
wants lines that did not match, the sense of the matches is inverted
using the `!' operator. fcount is incremented with the value of
matches, which will be either one or zero, depending upon a
successful or unsuccessful match. If the line did not match, the
next statement just moves on to the next record.
There are several optimizations for performance in the following few lines
of code. If the user only wants exit status (no_print is true), and
we don't have to count lines, then it is enough to know that one line in
this file matched, and we can skip on to the next file with nextfile.
Along similar lines, if we are only printing file names, and we
don't need to count lines, we can print the file name, and then skip to the
next file with nextfile.
Finally, each line is printed, with a leading filename and colon if necessary.
{
matches = ($0 ~ pattern)
if (invert)
matches = ! matches
fcount += matches # 1 or 0
if (! matches)
next
if (no_print && ! count_only)
nextfile
if (filenames_only && ! count_only) {
print FILENAME
nextfile
}
if (do_filenames && ! count_only)
print FILENAME ":" $0
else if (! count_only)
print
}
The END rule takes care of producing the correct exit status. If
there were no matches, the exit status is one, otherwise it is zero.
END \
{
if (total == 0)
exit 1
exit 0
}
The usage function prints a usage message in case of invalid options
and then exits.
function usage( e)
{
e = "Usage: egrep [-csvil] [-e pat] [files ...]"
print e > "/dev/stderr"
exit 1
}
The variable e is used so that the function fits nicely
on the printed page.
Just a note on programming style. You may have noticed that the END
rule uses backslash continuation, with the open brace on a line by
itself. This is so that it more closely resembles the way functions
are written. Many of the examples
in this chapter
use this style. You can decide for yourself if you like writing
your BEGIN and END rules this way,
or not.
The id utility lists a user's real and effective user-id numbers,
real and effective group-id numbers, and the user's group set, if any.
id will only print the effective user-id and group-id if they are
different from the real ones. If possible, id will also supply the
corresponding user and group names. The output might look like this:
$ id -| uid=2076(arnold) gid=10(staff) groups=10(staff),4(tty)
This information is exactly what is provided by gawk's
`/dev/user' special file (see section Speciális file nevek gawk-ban).
However, the id utility provides a more palatable output than just a
string of numbers.
Here is a simple version of id written in awk.
It uses the user database library functions
(see section Reading the User Database),
and the group database library functions
(see section Reading the Group Database).
The program is fairly straightforward. All the work is done in the
BEGIN rule. The user and group id numbers are obtained from
`/dev/user'. If there is no support for `/dev/user', the program
gives up.
The code is repetitive. The entry in the user database for the real user-id number is split into parts at the `:'. The name is the first field. Similar code is used for the effective user-id number, and the group numbers.
# id.awk --- implement id in awk
# Arnold Robbins, arnold@gnu.org, Public Domain
# May 1993
# output is:
# uid=12(foo) euid=34(bar) gid=3(baz) \
# egid=5(blat) groups=9(nine),2(two),1(one)
BEGIN \
{
if ((getline < "/dev/user") < 0) {
err = "id: no /dev/user support - cannot run"
print err > "/dev/stderr"
exit 1
}
close("/dev/user")
uid = $1
euid = $2
gid = $3
egid = $4
printf("uid=%d", uid)
pw = getpwuid(uid)
if (pw != "") {
split(pw, a, ":")
printf("(%s)", a[1])
}
if (euid != uid) {
printf(" euid=%d", euid)
pw = getpwuid(euid)
if (pw != "") {
split(pw, a, ":")
printf("(%s)", a[1])
}
}
printf(" gid=%d", gid)
pw = getgrgid(gid)
if (pw != "") {
split(pw, a, ":")
printf("(%s)", a[1])
}
if (egid != gid) {
printf(" egid=%d", egid)
pw = getgrgid(egid)
if (pw != "") {
split(pw, a, ":")
printf("(%s)", a[1])
}
}
if (NF > 4) {
printf(" groups=");
for (i = 5; i <= NF; i++) {
printf("%d", $i)
pw = getgrgid($i)
if (pw != "") {
split(pw, a, ":")
printf("(%s)", a[1])
}
if (i < NF)
printf(",")
}
}
print ""
}
The split program splits large text files into smaller pieces. By default,
the output files are named `xaa', `xab', and so on. Each file has
1000 lines in it, with the likely exception of the last file. To change the
number of lines in each file, you supply a number on the command line
preceded with a minus, e.g., `-500' for files with 500 lines in them
instead of 1000. To change the name of the output files to something like
`myfileaa', `myfileab', and so on, you supply an additional
argument that specifies the filename.
Here is a version of split in awk. It uses the ord and
chr functions presented in
section Translating Between Characters and Numbers.
The program first sets its defaults, and then tests to make sure there are not too many arguments. It then looks at each argument in turn. The first argument could be a minus followed by a number. If it is, this happens to look like a negative number, so it is made positive, and that is the count of lines. The data file name is skipped over, and the final argument is used as the prefix for the output file names.
# split.awk --- do split in awk
# Arnold Robbins, arnold@gnu.org, Public Domain
# May 1993
# usage: split [-num] [file] [outname]
BEGIN {
outfile = "x" # default
count = 1000
if (ARGC > 4)
usage()
i = 1
if (ARGV[i] ~ /^-[0-9]+$/) {
count = -ARGV[i]
ARGV[i] = ""
i++
}
# test argv in case reading from stdin instead of file
if (i in ARGV)
i++ # skip data file name
if (i in ARGV) {
outfile = ARGV[i]
ARGV[i] = ""
}
s1 = s2 = "a"
out = (outfile s1 s2)
}
The next rule does most of the work. tcount (temporary count) tracks
how many lines have been printed to the output file so far. If it is greater
than count, it is time to close the current file and start a new one.
s1 and s2 track the current suffixes for the file name. If
they are both `z', the file is just too big. Otherwise, s1
moves to the next letter in the alphabet and s2 starts over again at
`a'.
{
if (++tcount > count) {
close(out)
if (s2 == "z") {
if (s1 == "z") {
printf("split: %s is too large to split\n", \
FILENAME) > "/dev/stderr"
exit 1
}
s1 = chr(ord(s1) + 1)
s2 = "a"
} else
s2 = chr(ord(s2) + 1)
out = (outfile s1 s2)
tcount = 1
}
print > out
}
The usage function simply prints an error message and exits.
function usage( e)
{
e = "usage: split [-num] [file] [outname]"
print e > "/dev/stderr"
exit 1
}
The variable e is used so that the function
fits nicely on the
page.
This program is a bit sloppy; it relies on awk to close the last file
for it automatically, instead of doing it in an END rule.
The tee program is known as a "pipe fitting." tee copies
its standard input to its standard output, and also duplicates it to the
files named on the command line. Its usage is:
tee [-a] file ...
The `-a' option tells tee to append to the named files, instead of
truncating them and starting over.
The BEGIN rule first makes a copy of all the command line arguments,
into an array named copy.
ARGV[0] is not copied, since it is not needed.
tee cannot use ARGV directly, since awk will attempt to
process each file named in ARGV as input data.
If the first argument is `-a', then the flag variable
append is set to true, and both ARGV[1] and
copy[1] are deleted. If ARGC is less than two, then no file
names were supplied, and tee prints a usage message and exits.
Finally, awk is forced to read the standard input by setting
ARGV[1] to "-", and ARGC to two.
# tee.awk --- tee in awk
# Arnold Robbins, arnold@gnu.org, Public Domain
# May 1993
# Revised December 1995
BEGIN \
{
for (i = 1; i < ARGC; i++)
copy[i] = ARGV[i]
if (ARGV[1] == "-a") {
append = 1
delete ARGV[1]
delete copy[1]
ARGC--
}
if (ARGC < 2) {
print "usage: tee [-a] file ..." > "/dev/stderr"
exit 1
}
ARGV[1] = "-"
ARGC = 2
}
The single rule does all the work. Since there is no pattern, it is executed for each line of input. The body of the rule simply prints the line into each file on the command line, and then to the standard output.
{
# moving the if outside the loop makes it run faster
if (append)
for (i in copy)
print >> copy[i]
else
for (i in copy)
print > copy[i]
print
}
It would have been possible to code the loop this way:
for (i in copy)
if (append)
print >> copy[i]
else
print > copy[i]
This is more concise, but it is also less efficient. The `if' is
tested for each record and for each output file. By duplicating the loop
body, the `if' is only tested once for each input record. If there are
N input records and M input files, the first method only
executes N `if' statements, while the second would execute
N*M `if' statements.
Finally, the END rule cleans up, by closing all the output files.
END \
{
for (i in copy)
close(copy[i])
}
The uniq utility reads sorted lines of data on its standard input,
and (by default) removes duplicate lines. In other words, only unique lines
are printed, hence the name. uniq has a number of options. The usage is:
uniq [-udc [-n]] [+n] [ input file [ output file ]]
The option meanings are:
-d
-u
-c
-n
awk's default: non-whitespace characters separated
by runs of spaces and/or tabs.
+n
input file
output file
Normally uniq behaves as if both the `-d' and `-u' options
had been provided.
Here is an awk implementation of uniq. It uses the
getopt library function
(see section Processing Command Line Options),
and the join library function
(see section Merging an Array Into a String).
The program begins with a usage function and then a brief outline of
the options and their meanings in a comment.
The BEGIN rule deals with the command line arguments and options. It
uses a trick to get getopt to handle options of the form `-25',
treating such an option as the option letter `2' with an argument of
`5'. If indeed two or more digits were supplied (Optarg looks
like a number), Optarg is
concatenated with the option digit, and then result is added to zero to make
it into a number. If there is only one digit in the option, then
Optarg is not needed, and Optind must be decremented so that
getopt will process it next time. This code is admittedly a bit
tricky.
If no options were supplied, then the default is taken, to print both
repeated and non-repeated lines. The output file, if provided, is assigned
to outputfile. Earlier, outputfile was initialized to the
standard output, `/dev/stdout'.
# uniq.awk --- do uniq in awk
# Arnold Robbins, arnold@gnu.org, Public Domain
# May 1993
function usage( e)
{
e = "Usage: uniq [-udc [-n]] [+n] [ in [ out ]]"
print e > "/dev/stderr"
exit 1
}
# -c count lines. overrides -d and -u
# -d only repeated lines
# -u only non-repeated lines
# -n skip n fields
# +n skip n characters, skip fields first
BEGIN \
{
count = 1
outputfile = "/dev/stdout"
opts = "udc0:1:2:3:4:5:6:7:8:9:"
while ((c = getopt(ARGC, ARGV, opts)) != -1) {
if (c == "u")
non_repeated_only++
else if (c == "d")
repeated_only++
else if (c == "c")
do_count++
else if (index("0123456789", c) != 0) {
# getopt requires args to options
# this messes us up for things like -5
if (Optarg ~ /^[0-9]+$/)
fcount = (c Optarg) + 0
else {
fcount = c + 0
Optind--
}
} else
usage()
}
if (ARGV[Optind] ~ /^\+[0-9]+$/) {
charcount = substr(ARGV[Optind], 2) + 0
Optind++
}
for (i = 1; i < Optind; i++)
ARGV[i] = ""
if (repeated_only == 0 && non_repeated_only == 0)
repeated_only = non_repeated_only = 1
if (ARGC - Optind == 2) {
outputfile = ARGV[ARGC - 1]
ARGV[ARGC - 1] = ""
}
}
The following function, are_equal, compares the current line,
$0, to the
previous line, last. It handles skipping fields and characters.
If no field count and no character count were specified, are_equal
simply returns one or zero depending upon the result of a simple string
comparison of last and $0. Otherwise, things get more
complicated.
If fields have to be skipped, each line is broken into an array using
split
(see section Szövegmanipuláló beépített függvények),
and then the desired fields are joined back into a line using join.
The joined lines are stored in clast and cline.
If no fields are skipped, clast and cline are set to
last and $0 respectively.
Finally, if characters are skipped, substr is used to strip off the
leading charcount characters in clast and cline. The
two strings are then compared, and are_equal returns the result.
function are_equal( n, m, clast, cline, alast, aline)
{
if (fcount == 0 && charcount == 0)
return (last == $0)
if (fcount > 0) {
n = split(last, alast)
m = split($0, aline)
clast = join(alast, fcount+1, n)
cline = join(aline, fcount+1, m)
} else {
clast = last
cline = $0
}
if (charcount) {
clast = substr(clast, charcount + 1)
cline = substr(cline, charcount + 1)
}
return (clast == cline)
}
The following two rules are the body of the program. The first one is
executed only for the very first line of data. It sets last equal to
$0, so that subsequent lines of text have something to be compared to.
The second rule does the work. The variable equal will be one or zero
depending upon the results of are_equal's comparison. If uniq
is counting repeated lines, then the count variable is incremented if
the lines are equal. Otherwise the line is printed and count is
reset, since the two lines are not equal.
If uniq is not counting, count is incremented if the lines are
equal. Otherwise, if uniq is counting repeated lines, and more than
one line has been seen, or if uniq is counting non-repeated lines,
and only one line has been seen, then the line is printed, and count
is reset.
Finally, similar logic is used in the END rule to print the final
line of input data.
NR == 1 {
last = $0
next
}
{
equal = are_equal()
if (do_count) { # overrides -d and -u
if (equal)
count++
else {
printf("%4d %s\n", count, last) > outputfile
last = $0
count = 1 # reset
}
next
}
if (equal)
count++
else {
if ((repeated_only && count > 1) ||
(non_repeated_only && count == 1))
print last > outputfile
last = $0
count = 1
}
}
END {
if (do_count)
printf("%4d %s\n", count, last) > outputfile
else if ((repeated_only && count > 1) ||
(non_repeated_only && count == 1))
print last > outputfile
}
The wc (word count) utility counts lines, words, and characters in
one or more input files. Its usage is:
wc [-lwc] [ files ... ]
If no files are specified on the command line, wc reads its standard
input. If there are multiple files, it will also print total counts for all
the files. The options and their meanings are:
-l
-w
awk separates
fields in its input data.
-c
Implementing wc in awk is particularly elegant, since
awk does a lot of the work for us; it splits lines into words (i.e.
fields) and counts them, it counts lines (i.e. records) for us, and it can
easily tell us how long a line is.
This version uses the getopt library function
(see section Processing Command Line Options),
and the file transition functions
(see section Noting Data File Boundaries).
This version has one major difference from traditional versions of wc.
Our version always prints the counts in the order lines, words,
and characters. Traditional versions note the order of the `-l',
`-w', and `-c' options on the command line, and print the counts
in that order.
The BEGIN rule does the argument processing.
The variable print_total will
be true if more than one file was named on the command line.
# wc.awk --- count lines, words, characters
# Arnold Robbins, arnold@gnu.org, Public Domain
# May 1993
# Options:
# -l only count lines
# -w only count words
# -c only count characters
#
# Default is to count lines, words, characters
BEGIN {
# let getopt print a message about
# invalid options. we ignore them
while ((c = getopt(ARGC, ARGV, "lwc")) != -1) {
if (c == "l")
do_lines = 1
else if (c == "w")
do_words = 1
else if (c == "c")
do_chars = 1
}
for (i = 1; i < Optind; i++)
ARGV[i] = ""
# if no options, do all
if (! do_lines && ! do_words && ! do_chars)
do_lines = do_words = do_chars = 1
print_total = (ARGC - i > 2)
}
The beginfile function is simple; it just resets the counts of lines,
words, and characters to zero, and saves the current file name in
fname.
The endfile function adds the current file's numbers to the running
totals of lines, words, and characters. It then prints out those numbers
for the file that was just read. It relies on beginfile to reset the
numbers for the following data file.
function beginfile(file)
{
chars = lines = words = 0
fname = FILENAME
}
function endfile(file)
{
tchars += chars
tlines += lines
twords += words
if (do_lines)
printf "\t%d", lines
if (do_words)
printf "\t%d", words
if (do_chars)
printf "\t%d", chars
printf "\t%s\n", fname
}
There is one rule that is executed for each line. It adds the length of the
record to chars. It has to add one, since the newline character
separating records (the value of RS) is not part of the record
itself. lines is incremented for each line read, and words is
incremented by the value of NF, the number of "words" on this
line.(23)
Finally, the END rule simply prints the totals for all the files.
# do per line
{
chars += length($0) + 1 # get newline
lines++
words += NF
}
END {
if (print_total) {
if (do_lines)
printf "\t%d", tlines
if (do_words)
printf "\t%d", twords
if (do_chars)
printf "\t%d", tchars
print "\ttotal"
}
}
awk ProgramsThis section is a large "grab bag" of miscellaneous programs. We hope you find them both interesting and enjoyable.
A common error when writing large amounts of prose is to accidentally duplicate words. Often you will see this in text as something like "the the program does the following ...." When the text is on-line, often the duplicated words occur at the end of one line and the beginning of another, making them very difficult to spot.
This program, `dupword.awk', scans through a file one line at a time,
and looks for adjacent occurrences of the same word. It also saves the last
word on a line (in the variable prev) for comparison with the first
word on the next line.
The first two statements make sure that the line is all lower-case, so that, for example, "The" and "the" compare equal to each other. The second statement removes all non-alphanumeric and non-whitespace characters from the line, so that punctuation does not affect the comparison either. This sometimes leads to reports of duplicated words that really are different, but this is unusual.
# dupword --- find duplicate words in text
# Arnold Robbins, arnold@gnu.org, Public Domain
# December 1991
{
$0 = tolower($0)
gsub(/[^A-Za-z0-9 \t]/, "");
if ($1 == prev)
printf("%s:%d: duplicate %s\n",
FILENAME, FNR, $1)
for (i = 2; i <= NF; i++)
if ($i == $(i-1))
printf("%s:%d: duplicate %s\n",
FILENAME, FNR, $i)
prev = $NF
}
The following program is a simple "alarm clock" program. You give it a time of day, and an optional message. At the given time, it prints the message on the standard output. In addition, you can give it the number of times to repeat the message, and also a delay between repetitions.
This program uses the gettimeofday function from
section Managing the Time of Day.
All the work is done in the BEGIN rule. The first part is argument
checking and setting of defaults; the delay, the count, and the message to
print. If the user supplied a message, but it does not contain the ASCII BEL
character (known as the "alert" character, `\a'), then it is added to
the message. (On many systems, printing the ASCII BEL generates some sort
of audible alert. Thus, when the alarm goes off, the system calls attention
to itself, in case the user is not looking at their computer or terminal.)
# alarm --- set an alarm
# Arnold Robbins, arnold@gnu.org, Public Domain
# May 1993
# usage: alarm time [ "message" [ count [ delay ] ] ]
BEGIN \
{
# Initial argument sanity checking
usage1 = "usage: alarm time ['message' [count [delay]]]"
usage2 = sprintf("\t(%s) time ::= hh:mm", ARGV[1])
if (ARGC < 2) {
print usage > "/dev/stderr"
exit 1
} else if (ARGC == 5) {
delay = ARGV[4] + 0
count = ARGV[3] + 0
message = ARGV[2]
} else if (ARGC == 4) {
count = ARGV[3] + 0
message = ARGV[2]
} else if (ARGC == 3) {
message = ARGV[2]
} else if (ARGV[1] !~ /[0-9]?[0-9]:[0-9][0-9]/) {
print usage1 > "/dev/stderr"
print usage2 > "/dev/stderr"
exit 1
}
# set defaults for once we reach the desired time
if (delay == 0)
delay = 180 # 3 minutes
if (count == 0)
count = 5
if (message == "")
message = sprintf("\aIt is now %s!\a", ARGV[1])
else if (index(message, "\a") == 0)
message = "\a" message "\a"
The next section of code turns the alarm time into hours and minutes, and converts it if necessary to a 24-hour clock. Then it turns that time into a count of the seconds since midnight. Next it turns the current time into a count of seconds since midnight. The difference between the two is how long to wait before setting off the alarm.
# split up dest time
split(ARGV[1], atime, ":")
hour = atime[1] + 0 # force numeric
minute = atime[2] + 0 # force numeric
# get current broken down time
gettimeofday(now)
# if time given is 12-hour hours and it's after that
# hour, e.g., `alarm 5:30' at 9 a.m. means 5:30 p.m.,
# then add 12 to real hour
if (hour < 12 && now["hour"] > hour)
hour += 12
# set target time in seconds since midnight
target = (hour * 60 * 60) + (minute * 60)
# get current time in seconds since midnight
current = (now["hour"] * 60 * 60) + \
(now["minute"] * 60) + now["second"]
# how long to sleep for
naptime = target - current
if (naptime <= 0) {
print "time is in the past!" > "/dev/stderr"
exit 1
}
Finally, the program uses the system function
(see section Bemenettel és kimenettel (I/O) kapcsolatos beépített függvények)
to call the sleep utility. The sleep utility simply pauses
for the given number of seconds. If the exit status is not zero,
the program assumes that sleep was interrupted, and exits. If
sleep exited with an OK status (zero), then the program prints the
message in a loop, again using sleep to delay for however many
seconds are necessary.
# zzzzzz..... go away if interrupted
if (system(sprintf("sleep %d", naptime)) != 0)
exit 1
# time to notify!
command = sprintf("sleep %d", delay)
for (i = 1; i <= count; i++) {
print message
# if sleep command interrupted, go away
if (system(command) != 0)
break
}
exit 0
}
The system tr utility transliterates characters. For example, it is
often used to map upper-case letters into lower-case, for further
processing.
generate data | tr '[A-Z]' '[a-z]' | process data ...
You give tr two lists of characters enclosed in square brackets.
Usually, the lists are quoted to keep the shell from attempting to do a
filename expansion.(24) When processing the input, the
first character in the first list is replaced with the first character in the
second list, the second character in the first list is replaced with the
second character in the second list, and so on.
If there are more characters in the "from" list than in the "to" list,
the last character of the "to" list is used for the remaining characters
in the "from" list.
Some time ago,
a user proposed to us that we add a transliteration function to gawk.
Being opposed to "creeping featurism," I wrote the following program to
prove that character transliteration could be done with a user-level
function. This program is not as complete as the system tr utility,
but it will do most of the job.
The translate program demonstrates one of the few weaknesses of
standard
awk: dealing with individual characters is very painful, requiring
repeated use of the substr, index, and gsub built-in
functions
(see section Szövegmanipuláló beépített függvények).(25)
There are two functions. The first, stranslate, takes three
arguments.
from
to
target
Associative arrays make the translation part fairly easy. t_ar holds
the "to" characters, indexed by the "from" characters. Then a simple
loop goes through from, one character at a time. For each character
in from, if the character appears in target, gsub
is used to change it to the corresponding to character.
The translate function simply calls stranslate using $0
as the target. The main program sets two global variables, FROM and
TO, from the command line, and then changes ARGV so that
awk will read from the standard input.
Finally, the processing rule simply calls translate for each record.
# translate --- do tr like stuff
# Arnold Robbins, arnold@gnu.org, Public Domain
# August 1989
# bugs: does not handle things like: tr A-Z a-z, it has
# to be spelled out. However, if `to' is shorter than `from',
# the last character in `to' is used for the rest of `from'.
function stranslate(from, to, target, lf, lt, t_ar, i, c)
{
lf = length(from)
lt = length(to)
for (i = 1; i <= lt; i++)
t_ar[substr(from, i, 1)] = substr(to, i, 1)
if (lt < lf)
for (; i <= lf; i++)
t_ar[substr(from, i, 1)] = substr(to, lt, 1)
for (i = 1; i <= lf; i++) {
c = substr(from, i, 1)
if (index(target, c) > 0)
gsub(c, t_ar[c], target)
}
return target
}
function translate(from, to)
{
return $0 = stranslate(from, to, $0)
}
# main program
BEGIN {
if (ARGC < 3) {
print "usage: translate from to" > "/dev/stderr"
exit
}
FROM = ARGV[1]
TO = ARGV[2]
ARGC = 2
ARGV[1] = "-"
}
{
translate(FROM, TO)
print
}
While it is possible to do character transliteration in a user-level
function, it is not necessarily efficient, and we started to consider adding
a built-in function. However, shortly after writing this program, we learned
that the System V Release 4 awk had added the toupper and
tolower functions. These functions handle the vast majority of the
cases where character transliteration is necessary, and so we chose to
simply add those functions to gawk as well, and then leave well
enough alone.
An obvious improvement to this program would be to set up the
t_ar array only once, in a BEGIN rule. However, this
assumes that the "from" and "to" lists
will never change throughout the lifetime of the program.
Here is a "real world"(26) program. This script reads lists of names and addresses, and generates mailing labels. Each page of labels has 20 labels on it, two across and ten down. The addresses are guaranteed to be no more than five lines of data. Each address is separated from the next by a blank line.
The basic idea is to read 20 labels worth of data. Each line of each label
is stored in the line array. The single rule takes care of filling
the line array and printing the page when 20 labels have been read.
The BEGIN rule simply sets RS to the empty string, so that
awk will split records at blank lines
(see section Hogyan történik a feldarabolás rekordokra).
It sets MAXLINES to 100, since MAXLINE is the maximum number
of lines on the page (20 * 5 = 100).
Most of the work is done in the printpage function.
The label lines are stored sequentially in the line array. But they
have to be printed horizontally; line[1] next to line[6],
line[2] next to line[7], and so on. Two loops are used to
accomplish this. The outer loop, controlled by i, steps through
every 10 lines of data; this is each row of labels. The inner loop,
controlled by j, goes through the lines within the row.
As j goes from zero to four, `i+j' is the j'th line in
the row, and `i+j+5' is the entry next to it. The output ends up
looking something like this:
line 1 line 6 line 2 line 7 line 3 line 8 line 4 line 9 line 5 line 10
As a final note, at lines 21 and 61, an extra blank line is printed, to keep the output lined up on the labels. This is dependent on the particular brand of labels in use when the program was written. You will also note that there are two blank lines at the top and two blank lines at the bottom.
The END rule arranges to flush the final page of labels; there may
not have been an even multiple of 20 labels in the data.
# labels.awk
# Arnold Robbins, arnold@gnu.org, Public Domain
# June 1992
# Program to print labels. Each label is 5 lines of data
# that may have blank lines. The label sheets have 2
# blank lines at the top and 2 at the bottom.
BEGIN { RS = "" ; MAXLINES = 100 }
function printpage( i, j)
{
if (Nlines <= 0)
return
printf "\n\n" # header
for (i = 1; i <= Nlines; i += 10) {
if (i == 21 || i == 61)
print ""
for (j = 0; j < 5; j++) {
if (i + j > MAXLINES)
break
printf " %-41s %s\n", line[i+j], line[i+j+5]
}
print ""
}
printf "\n\n" # footer
for (i in line)
line[i] = ""
}
# main rule
{
if (Count >= 20) {
printpage()
Count = 0
Nlines = 0
}
n = split($0, a, "\n")
for (i = 1; i <= n; i++)
line[++Nlines] = a[i]
for (; i <= 5; i++)
line[++Nlines] = ""
Count++
}
END \
{
printpage()
}
The following awk program prints
the number of occurrences of each word in its input. It illustrates the
associative nature of awk arrays by using strings as subscripts. It
also demonstrates the `for x in array' construction.
Finally, it shows how awk can be used in conjunction with other
utility programs to do a useful task of some complexity with a minimum of
effort. Some explanations follow the program listing.
awk '
# Print list of word frequencies
{
for (i = 1; i <= NF; i++)
freq[$i]++
}
END {
for (word in freq)
printf "%s\t%d\n", word, freq[word]
}'
The first thing to notice about this program is that it has two rules. The
first rule, because it has an empty pattern, is executed on every line of
the input. It uses awk's field-accessing mechanism
(see section Mezők elérése) to pick out the individual words from
the line, and the built-in variable NF (see section Beépített változók)
to know how many fields are available.
For each input word, an element of the array freq is incremented to
reflect that the word has been seen an additional time.
The second rule, because it has the pattern END, is not executed
until the input has been exhausted. It prints out the contents of the
freq table that has been built up inside the first action.
This program has several problems that would prevent it from being useful by itself on real text files:
awk convention that fields are
separated by whitespace and that other characters in the input (except
newlines) don't have any special meaning to awk. This means that
punctuation characters count as part of words.
awk language considers upper- and lower-case characters to be
distinct. Therefore, `bartender' and `Bartender' are not treated
as the same word. This is undesirable since, in normal text, words
are capitalized if they begin sentences, and a frequency analyzer should not
be sensitive to capitalization.
The way to solve these problems is to use some of the more advanced
features of the awk language. First, we use tolower to remove
case distinctions. Next, we use gsub to remove punctuation
characters. Finally, we use the system sort utility to process the
output of the awk script. Here is the new version of
the program:
# Print list of word frequencies
{
$0 = tolower($0) # remove case distinctions
gsub(/[^a-z0-9_ \t]/, "", $0) # remove punctuation
for (i = 1; i <= NF; i++)
freq[$i]++
}
END {
for (word in freq)
printf "%s\t%d\n", word, freq[word]
}
Assuming we have saved this program in a file named `wordfreq.awk', and that the data is in `file1', the following pipeline
awk -f wordfreq.awk file1 | sort +1 -nr
produces a table of the words appearing in `file1' in order of decreasing frequency.
The awk program suitably massages the data and produces a word
frequency table, which is not ordered.
The awk script's output is then sorted by the sort utility and
printed on the terminal. The options given to sort in this example
specify to sort using the second field of each input line (skipping one field),
that the sort keys should be treated as numeric quantities (otherwise
`15' would come before `5'), and that the sorting should be done
in descending (reverse) order.
We could have even done the sort from within the program, by
changing the END action to:
END {
sort = "sort +1 -nr"
for (word in freq)
printf "%s\t%d\n", word, freq[word] | sort
close(sort)
}
You would have to use this way of sorting on systems that do not have true pipes.
See the general operating system documentation for more information on how
to use the sort program.
The uniq program
(see section Printing Non-duplicated Lines of Text),
removes duplicate lines from sorted data.
Suppose, however, you need to remove duplicate lines from a data file, but that you wish to preserve the order the lines are in? A good example of this might be a shell history file. The history file keeps a copy of all the commands you have entered, and it is not unusual to repeat a command several times in a row. Occasionally you might wish to compact the history by removing duplicate entries. Yet it is desirable to maintain the order of the original commands.
This simple program does the job. It uses two arrays. The data
array is indexed by the text of each line.
For each line, data[$0] is incremented.
If a particular line has not
been seen before, then data[$0] will be zero.
In that case, the text of the line is stored in lines[count].
Each element of lines is a unique command, and the indices of
lines indicate the order in which those lines were encountered.
The END rule simply prints out the lines, in order.
# histsort.awk --- compact a shell history file
# Arnold Robbins, arnold@gnu.org, Public Domain
# May 1993
# Thanks to Byron Rakitzis for the general idea
{
if (data[$0]++ == 0)
lines[++count] = $0
}
END {
for (i = 1; i <= count; i++)
print lines[i]
}
This program also provides a foundation for generating other useful
information. For example, using the following print satement in the
END rule would indicate how often a particular command was used.
print data[lines[i]], lines[i]
This works because data[$0] was incremented each time a line was
seen.
Both this chapter and the previous chapter
(section A Library of awk Functions),
present a large number of awk programs.
If you wish to experiment with these programs, it is tedious to have to type
them in by hand. Here we present a program that can extract parts of a
Texinfo input file into separate files.
This könyv is written in Texinfo, the GNU project's document formatting language. A single Texinfo source file can be used to produce both printed and on-line documentation. Texinfo is fully documented in Texinfo--The GNU Documentation Format, available from the Free Software Foundation.
For our purposes, it is enough to know three things about Texinfo input files.
awk. Literal `@' symbols are represented in Texinfo source
files as `@@'.
The following program, `extract.awk', reads through a Texinfo source
file, and does two things, based on the special comments.
Upon seeing `@c system ...',
it runs a command, by extracting the command text from the
control line and passing it on to the system function
(see section Bemenettel és kimenettel (I/O) kapcsolatos beépített függvények).
Upon seeing `@c file filename', each subsequent line is sent to
the file filename, until `@c endfile' is encountered.
The rules in `extract.awk' will match either `@c' or
`@comment' by letting the `omment' part be optional.
Lines containing `@group' and `@end group' are simply removed.
`extract.awk' uses the join library function
(see section Merging an Array Into a String).
The example programs in the on-line Texinfo source for Hatékony AWK programozás
(`gawk.texi') have all been bracketed inside `file',
and `endfile' lines. The gawk distribution uses a copy of
`extract.awk' to extract the sample
programs and install many of them in a standard directory, where
gawk can find them.
The Texinfo file looks something like this:
...
This program has a @code{BEGIN} block,
which prints a nice message:
@example
@c file examples/messages.awk
BEGIN @{ print "Don't panic!" @}
@c end file
@end example
It also prints some final advice:
@example
@c file examples/messages.awk
END @{ print "Always avoid bored archeologists!" @}
@c end file
@end example
...
`extract.awk' begins by setting IGNORECASE to one, so that
mixed upper-case and lower-case letters in the directives won't matter.
The first rule handles calling system, checking that a command was
given (NF is at least three), and also checking that the command
exited with a zero exit status, signifying OK.
# extract.awk --- extract files and run programs
# from texinfo files
# Arnold Robbins, arnold@gnu.org, Public Domain
# May 1993
BEGIN { IGNORECASE = 1 }
/^@c(omment)?[ \t]+system/ \
{
if (NF < 3) {
e = (FILENAME ":" FNR)
e = (e ": badly formed `system' line")
print e > "/dev/stderr"
next
}
$1 = ""
$2 = ""
stat = system($0)
if (stat != 0) {
e = (FILENAME ":" FNR)
e = (e ": warning: system returned " stat)
print e > "/dev/stderr"
}
}
The variable e is used so that the function
fits nicely on the
page.
The second rule handles moving data into files. It verifies that a file name was given in the directive. If the file named is not the current file, then the current file is closed. This means that an `@c endfile' was not given for that file. (We should probably print a diagnostic in this case, although at the moment we do not.)
The `for' loop does the work. It reads lines using getline
(see section Explicit beolvasás getline-al).
For an unexpected end of file, it calls the unexpected_eof
function. If the line is an "endfile" line, then it breaks out of
the loop.
If the line is an `@group' or `@end group' line, then it
ignores it, and goes on to the next line.
(These Texinfo control lines keep blocks of code together on one page;
unfortunately, TeX isn't always smart enough to do things exactly right,
and we have to give it some advice.)
Most of the work is in the following few lines. If the line has no `@' symbols, it can be printed directly. Otherwise, each leading `@' must be stripped off.
To remove the `@' symbols, the line is split into separate elements of
the array a, using the split function
(see section Szövegmanipuláló beépített függvények).
Each element of a that is empty indicates two successive `@'
symbols in the original line. For each two empty elements (`@@' in
the original file), we have to add back in a single `@' symbol.
When the processing of the array is finished, join is called with the
value of SUBSEP, to rejoin the pieces back into a single
line. That line is then printed to the output file.
/^@c(omment)?[ \t]+file/ \
{
if (NF != 3) {
e = (FILENAME ":" FNR ": badly formed `file' line")
print e > "/dev/stderr"
next
}
if ($3 != curfile) {
if (curfile != "")
close(curfile)
curfile = $3
}
for (;;) {
if ((getline line) <= 0)
unexpected_eof()
if (line ~ /^@c(omment)?[ \t]+endfile/)
break
else if (line ~ /^@(end[ \t]+)?group/)
continue
if (index(line, "@") == 0) {
print line > curfile
continue
}
n = split(line, a, "@")
# if a[1] == "", means leading @,
# don't add one back in.
for (i = 2; i <= n; i++) {
if (a[i] == "") { # was an @@
a[i] = "@"
if (a[i+1] == "")
i++
}
}
print join(a, 1, n, SUBSEP) > curfile
}
}
An important thing to note is the use of the `>' redirection.
Output done with `>' only opens the file once; it stays open and
subsequent output is appended to the file
(see section A print és a printf kimenetének átirányítása).
This allows us to easily mix program text and explanatory prose for the same
sample source file (as has been done here!) without any hassle. The file is
only closed when a new data file name is encountered, or at the end of the
input file.
Finally, the function unexpected_eof prints an appropriate
error message and then exits.
The END rule handles the final cleanup, closing the open file.
function unexpected_eof()
{
printf("%s:%d: unexpected EOF or error\n", \
FILENAME, FNR) > "/dev/stderr"
exit 1
}
END {
if (curfile)
close(curfile)
}
The sed utility is a "stream editor," a program that reads a
stream of data, makes changes to it, and passes the modified data on.
It is often used to make global changes to a large file, or to a stream
of data generated by a pipeline of commands.
While sed is a complicated program in its own right, its most common
use is to perform global substitutions in the middle of a pipeline:
command1 < orig.data | sed 's/old/new/g' | command2 > result
Here, the `s/old/new/g' tells sed to look for the regexp
`old' on each input line, and replace it with the text `new',
globally (i.e. all the occurrences on a line). This is similar to
awk's gsub function
(see section Szövegmanipuláló beépített függvények).
The following program, `awksed.awk', accepts at least two command line arguments; the pattern to look for and the text to replace it with. Any additional arguments are treated as data file names to process. If none are provided, the standard input is used.
# awksed.awk --- do s/foo/bar/g using just print
# Thanks to Michael Brennan for the idea
# Arnold Robbins, arnold@gnu.org, Public Domain
# August 1995
function usage()
{
print "usage: awksed pat repl [files...]" > "/dev/stderr"
exit 1
}
BEGIN {
# validate arguments
if (ARGC < 3)
usage()
RS = ARGV[1]
ORS = ARGV[2]
# don't use arguments as files
ARGV[1] = ARGV[2] = ""
}
# look ma, no hands!
{
if (RT == "")
printf "%s", $0
else
print
}
The program relies on gawk's ability to have RS be a regexp
and on the setting of RT to the actual text that terminated the
record (see section Hogyan történik a feldarabolás rekordokra).
The idea is to have RS be the pattern to look for. gawk
will automatically set $0 to the text between matches of the pattern.
This is text that we wish to keep, unmodified. Then, by setting ORS
to the replacement text, a simple print statement will output the
text we wish to keep, followed by the replacement text.
There is one wrinkle to this scheme, which is what to do if the last record
doesn't end with text that matches RS? Using a print
statement unconditionally prints the replacement text, which is not correct.
However, if the file did not end in text that matches RS, RT
will be set to the null string. In this case, we can print $0 using
printf
(see section Nyomtatás a printf kifejezéssel).
The BEGIN rule handles the setup, checking for the right number
of arguments, and calling usage if there is a problem. Then it sets
RS and ORS from the command line arguments, and sets
ARGV[1] and ARGV[2] to the null string, so that they will
not be treated as file names
(see section Az ARGC és az ARGV változók használata).
The usage function prints an error message and exits.
Finally, the single rule handles the printing scheme outlined above,
using print or printf as appropriate, depending upon the
value of RT.
Using library functions in awk can be very beneficial. It
encourages code re-use and the writing of general functions. Programs are
smaller, and therefore clearer.
However, using library functions is only easy when writing awk
programs; it is painful when running them, requiring multiple `-f'
options. If gawk is unavailable, then so too is the AWKPATH
environment variable and the ability to put awk functions into a
library directory (see section Command Line Options).
It would be nice to be able to write programs like so:
# library functions
@include getopt.awk
@include join.awk
...
# main program
BEGIN {
while ((c = getopt(ARGC, ARGV, "a:b:cde")) != -1)
...
...
}
The following program, `igawk.sh', provides this service.
It simulates gawk's searching of the AWKPATH variable,
and also allows nested includes; i.e. a file that has been included
with `@include' can contain further `@include' statements.
igawk will make an effort to only include files once, so that nested
includes don't accidentally include a library function twice.
igawk should behave externally just like gawk. This means it
should accept all of gawk's command line arguments, including the
ability to have multiple source files specified via `-f', and the
ability to mix command line and library source files.
The program is written using the POSIX Shell (sh) command language.
The way the program works is as follows:
awk source code for later, when the expanded program is run.
awk text, put the arguments into
a temporary file that will be expanded. There are two cases.
echo program will automatically
supply a trailing newline.
gawk does, this will get the text
of the file included into the program at the correct point.
awk program (naturally) over the temporary file to expand
`@include' statements. The expanded program is placed in a second
temporary file.
gawk and any other original command line
arguments that the user supplied (such as the data file names).
The initial part of the program turns on shell tracing if the first
argument was `debug'. Otherwise, a shell trap statement
arranges to clean up any temporary files on program exit or upon an
interrupt.
The next part loops through all the command line arguments. There are several cases of interest.
--
igawk. Anything else should be passed on
to the user's awk program without being evaluated.
-W
gawk. To make
argument processing easier, the `-W' is appended to the front of the
remaining arguments and the loop continues. (This is an sh
programming trick. Don't worry about it if you are not familiar with
sh.)
-v
-F
gawk.
-f
--file
--file=
-Wfile=
sed utility is used to remove the leading option part of the
argument (e.g., `--file=').
--source
--source=
-Wsource=
--version
--version
-Wversion
igawk prints its version number, and runs `gawk --version'
to get the gawk version information, and then exits.
If none of `-f', `--file', `-Wfile', `--source',
or `-Wsource', were supplied, then the first non-option argument
should be the awk program. If there are no command line
arguments left, igawk prints an error message and exits.
Otherwise, the first argument is echoed into `/tmp/ig.s.$$'.
In any case, after the arguments have been processed,
`/tmp/ig.s.$$' contains the complete text of the original awk
program.
The `$$' in sh represents the current process ID number.
It is often used in shell programs to generate unique temporary file
names. This allows multiple users to run igawk without worrying
that the temporary file names will clash.
#! /bin/sh
# igawk --- like gawk but do @include processing
# Arnold Robbins, arnold@gnu.org, Public Domain
# July 1993
if [ "$1" = debug ]
then
set -x
shift
else
# cleanup on exit, hangup, interrupt, quit, termination
trap 'rm -f /tmp/ig.[se].$$' 0 1 2 3 15
fi
while [ $# -ne 0 ] # loop over arguments
do
case $1 in
--) shift; break;;
-W) shift
set -- -W"$@"
continue;;
-[vF]) opts="$opts $1 '$2'"
shift;;
-[vF]*) opts="$opts '$1'" ;;
-f) echo @include "$2" >> /tmp/ig.s.$$
shift;;
-f*) f=`echo "$1" | sed 's/-f//'`
echo @include "$f" >> /tmp/ig.s.$$ ;;
-?file=*) # -Wfile or --file
f=`echo "$1" | sed 's/-.file=//'`
echo @include "$f" >> /tmp/ig.s.$$ ;;
-?file) # get arg, $2
echo @include "$2" >> /tmp/ig.s.$$
shift;;
-?source=*) # -Wsource or --source
t=`echo "$1" | sed 's/-.source=//'`
echo "$t" >> /tmp/ig.s.$$ ;;
-?source) # get arg, $2
echo "$2" >> /tmp/ig.s.$$
shift;;
-?version)
echo igawk: version 1.0 1>&2
gawk --version
exit 0 ;;
-[W-]*) opts="$opts '$1'" ;;
*) break;;
esac
shift
done
if [ ! -s /tmp/ig.s.$$ ]
then
if [ -z "$1" ]
then
echo igawk: no program! 1>&2
exit 1
else
echo "$1" > /tmp/ig.s.$$
shift
fi
fi
# at this point, /tmp/ig.s.$$ has the program
The awk program to process `@include' directives reads through
the program, one line at a time using getline
(see section Explicit beolvasás getline-al).
The input file names and `@include' statements are managed using a
stack. As each `@include' is encountered, the current file name is
"pushed" onto the stack, and the file named in the `@include'
directive becomes
the current file name. As each file is finished, the stack is "popped,"
and the previous input file becomes the current input file again.
The process is started by making the original file the first one on the
stack.
The pathto function does the work of finding the full path to a
file. It simulates gawk's behavior when searching the AWKPATH
environment variable
(see section The AWKPATH Environment Variable).
If a file name has a `/' in it, no path search
is done. Otherwise, the file name is concatenated with the name of each
directory in the path, and an attempt is made to open the generated file
name. The only way in awk to test if a file can be read is to go
ahead and try to read it with getline; that is what pathto
does.(27)
If the file can be read, it is closed, and the file name is
returned.
gawk -- '
# process @include directives
function pathto(file, i, t, junk)
{
if (index(file, "/") != 0)
return file
for (i = 1; i <= ndirs; i++) {
t = (pathlist[i] "/" file)
if ((getline junk < t) > 0) {
# found it
close(t)
return t
}
}
return ""
}
The main program is contained inside one BEGIN rule. The first thing it
does is set up the pathlist array that pathto uses. After
splitting the path on `:', null elements are replaced with ".",
which represents the current directory.
BEGIN {
path = ENVIRON["AWKPATH"]
ndirs = split(path, pathlist, ":")
for (i = 1; i <= ndirs; i++) {
if (pathlist[i] == "")
pathlist[i] = "."
}
The stack is initialized with ARGV[1], which will be `/tmp/ig.s.$$'.
The main loop comes next. Input lines are read in succession. Lines that
do not start with `@include' are printed verbatim.
If the line does start with `@include', the file name is in $2.
pathto is called to generate the full path. If it could not, then we
print an error message and continue.
The next thing to check is if the file has been included already. The
processed array is indexed by the full file name of each included
file, and it tracks this information for us. If the file has been
seen, a warning message is printed. Otherwise, the new file name is
pushed onto the stack and processing continues.
Finally, when getline encounters the end of the input file, the file
is closed and the stack is popped. When stackptr is less than zero,
the program is done.
stackptr = 0
input[stackptr] = ARGV[1] # ARGV[1] is first file
for (; stackptr >= 0; stackptr--) {
while ((getline < input[stackptr]) > 0) {
if (tolower($1) != "@include") {
print
continue
}
fpath = pathto($2)
if (fpath == "") {
printf("igawk:%s:%d: cannot find %s\n", \
input[stackptr], FNR, $2) > "/dev/stderr"
continue
}
if (! (fpath in processed)) {
processed[fpath] = input[stackptr]
input[++stackptr] = fpath
} else
print $2, "included in", input[stackptr], \
"already included in", \
processed[fpath] > "/dev/stderr"
}
close(input[stackptr])
}
}' /tmp/ig.s.$$ > /tmp/ig.e.$$
The last step is to call gawk with the expanded program and the original
options and command line arguments that the user supplied. gawk's
exit status is passed back on to igawk's calling program.
eval gawk -f /tmp/ig.e.$$ $opts -- "$@" exit $?
This version of igawk represents my third attempt at this program.
There are three key simplifications that made the program work better.
awk program much simpler; all the
`@include' processing can be done once.
pathto function doesn't try to save the line read with
getline when testing for the file's accessibility. Trying to save
this line for use with the main program complicates things considerably.
getline loop in the BEGIN rule does it all in one
place. It is not necessary to call out to a separate loop for processing
nested `@include' statements.
Also, this program illustrates that it is often worthwhile to combine
sh and awk programming together. You can usually accomplish
quite a lot, without having to resort to low-level programming in C or C++, and it
is frequently easier to do certain kinds of string and argument manipulation
using the shell than it is in awk.
Finally, igawk shows that it is not always necessary to add new
features to a program; they can often be layered on top. With igawk,
there is no real reason to build `@include' processing into
gawk itself.
As an additional example of this, consider the idea of having two files in a directory in the search path.
getopt and assert.
gawk releases, without requiring the system administrator to
update it each time by adding the local functions.
One user
suggested that gawk be modified to automatically read these files
upon startup. Instead, it would be very simple to modify igawk
to do this. Since igawk can process nested `@include'
directives, `default.awk' could simply contain `@include'
statements for the desired library functions.
awk Language
This könyv describes the GNU implementation of awk, which follows
the POSIX specification. Many awk users are only familiar
with the original awk implementation in Version 7 Unix.
(This implementation was the basis for awk in Berkeley Unix,
through 4.3--Reno. The 4.4 release of Berkeley Unix uses gawk 2.15.2
for its version of awk.) This chapter briefly describes the
evolution of the awk language, with cross references to other parts
of the könyv where you can find more information.
The awk language evolved considerably between the release of
Version 7 Unix (1978) and the new version first made generally available in
System V Release 3.1 (1987). This section summarizes the changes, with
cross-references to further details.
awk kifejezések és sorok).
return statement
(see section Felhasználó által definiált függvények).
delete statement (see section A delete kifejezés).
do-while statement
(see section A do-while kifejezés).
atan2, cos, sin, rand and
srand (see section Numerikus beépített függvények).
gsub, sub, and match
(see section Szövegmanipuláló beépített függvények).
close, and system
(see section Bemenettel és kimenettel (I/O) kapcsolatos beépített függvények).
ARGC, ARGV, FNR, RLENGTH, RSTART,
and SUBSEP built-in variables (see section Beépített változók).
awk
programs (see section Operátorok precedenciája (Hogyan ágyazhatók operátorok egymásba)).
FS
(see section Hogyan történik a mezőelválasztás), and as the
third argument to the split function
(see section Szövegmanipuláló beépített függvények).
awk to
recognize `\r', `\b', and `\f', but this is not
something you can rely on.)
getline function
(see section Explicit beolvasás getline-al).
BEGIN and END rules
(see section A BEGIN és az END speciális minták).
The System V Release 4 version of Unix awk added these features
(some of which originated in gawk):
ENVIRON variable (see section Beépített változók).
srand built-in function
(see section Numerikus beépített függvények).
toupper and tolower built-in string functions
for case translation
(see section Szövegmanipuláló beépített függvények).
printf function
(see section Formátumleíró betűk).
"%*.*d")
in the argument list of the printf function
(see section Formátumleíró betűk).
/foo/ as expressions, where
they are equivalent to using the matching operator, as in `$0 ~ /foo/'
(see section Reguláris kifejezés konstansok használata).
awk
The POSIX Command Language and Utilities standard for awk
introduced the following changes into the language:
CONVFMT for controlling the conversion of numbers
to strings (see section Szövegek és számok konverziója).
The following common extensions are not permitted by the POSIX standard:
\x escape sequences are not recognized
(see section Escape szekvenciák).
FS is
equal to a single space.
func for the keyword function is not
recognized (see section A függvénydefiníció formája).
FS to be a single tab character
(see section Hogyan történik a mezőelválasztás).
fflush built-in function is not supported
(see section Bemenettel és kimenettel (I/O) kapcsolatos beépített függvények).
awk
Brian Kernighan, one of the original designers of Unix awk,
has made his version available via anonymous ftp
(see section Other Freely Available awk Implementations).
This section describes extensions in his version of awk that are
not in POSIX awk.
fflush built-in function for flushing buffered output
(see section Bemenettel és kimenettel (I/O) kapcsolatos beépített függvények).
gawk Not in POSIX awk
The GNU implementation, gawk, adds a number of features.
This sections lists them in the order they were added to gawk.
They can all be disabled with either the `--traditional' or
`--posix' options
(see section Command Line Options).
Version 2.10 of gawk introduced these features:
AWKPATH environment variable for specifying a path search for
the `-f' command line option
(see section Command Line Options).
IGNORECASE variable and its effects
(see section Kis- és nagybetűk az illesztésekben).
gawk-ban).
Version 2.13 of gawk introduced these features:
FIELDWIDTHS variable and its effects
(see section Meghatározott szélességű adatok beolvasása).
systime and strftime built-in functions for obtaining
and printing time stamps
(see section Dátumfeldolgozó függvények).
Version 2.14 of gawk introduced these features:
next file statement for skipping to the next data file
(see section A nextfile kifejezés).
Version 2.15 of gawk introduced these features:
ARGIND variable, that tracks the movement of FILENAME
through ARGV (see section Beépített változók).
ERRNO variable, that contains the system error message when
getline returns -1, or when close fails
(see section Beépített változók).
gawk-ban).
Version 3.0 of gawk introduced these features:
next file statement became nextfile
(see section A nextfile kifejezés).
awk
(see section Major Changes between V7 and SVR3.1).
FS to be a null string, and for the third
argument to split to be the null string
(see section Minden karakter egy mező).
RS to be a regexp
(see section Hogyan történik a feldarabolás rekordokra).
RT variable
(see section Hogyan történik a feldarabolás rekordokra).
gensub function for more powerful text manipulation
(see section Szövegmanipuláló beépített függvények).
strftime function acquired a default time format,
allowing it to be called with no arguments
(see section Dátumfeldolgozó függvények).
IGNORECASE changed, now applying to string comparison as well
as regexp operations
(see section Kis- és nagybetűk az illesztésekben).
fflush function from the
Bell Labs research version of awk
(see section Command Line Options; also
see section Bemenettel és kimenettel (I/O) kapcsolatos beépített függvények).
gawk for Unix).
gawk on an Amiga).
gawk Summary
This appendix provides a brief summary of the gawk command line and the
awk language. It is designed to serve as "quick reference." It is
therefore terse, but complete.
The command line consists of options to gawk itself, the
awk program text (if not supplied via the `-f' option), and
values to be made available in the ARGC and ARGV
predefined awk variables:
gawk [POSIX or GNU style options] -f source-file [--] file ... gawk [POSIX or GNU style options] [--] 'program' file ...
The options that gawk accepts are:
-F fs
--field-separator fs
FS
predefined variable).
-f program-file
--file program-file
awk program source from the file program-file, instead
of from the first command line argument.
-mf NNN
-mr NNN
gawk, since gawk
has no predefined limits; they are only for compatibility with the
Bell Labs research version of Unix awk.
-v var=val
--assign var=val
-W traditional
-W compat
--traditional
--compat
gawk extensions are turned
off.
-W copyleft
-W copyright
--copyleft
--copyright
gawk.
-W help
-W usage
--help
--usage
-W lint
--lint
awk constructs.
-W lint-old
--lint-old
awk.
-W posix
--posix
gawk extensions
are turned off and additional restrictions apply.
-W re-interval
--re-interval
-W source=program-text
--source program-text
awk program source code. This option allows
mixing command line source code with source code from files, and is
particularly useful for mixing command line programs with library functions.
-W version
--version
gawk on the error
output.
--
awk program itself to start with a `-'. This is mainly for
consistency with POSIX argument parsing conventions.
Any other options are flagged as invalid, but are otherwise ignored. See section Command Line Options, for more details.
An awk program consists of a sequence of zero or more pattern-action
statements and optional function definitions. One or the other of the
pattern and action may be omitted.
pattern { action statements }
pattern
{ action statements }
function name(parameter list) { action statements }
gawk first reads the program source from the
program-file(s), if specified, or from the first non-option
argument on the command line. The `-f' option may be used multiple
times on the command line. gawk reads the program text from all
the program-file files, effectively concatenating them in the
order they are specified. This is useful for building libraries of
awk functions, without having to include them in each new
awk program that uses them. To use a library function in a file
from a program typed in on the command line, specify
`--source 'program'', and type your program in between the single
quotes.
See section Command Line Options.
The environment variable AWKPATH specifies a search path to use
when finding source files named with the `-f' option. The default
path, which is
`.:/usr/local/share/awk'(28) is used if AWKPATH is not set.
If a file name given to the `-f' option contains a `/' character,
no path search is performed.
See section The AWKPATH Environment Variable.
gawk compiles the program into an internal form, and then proceeds to
read each file named in the ARGV array.
The initial values of ARGV come from the command line arguments.
If there are no files named
on the command line, gawk reads the standard input.
If a "file" named on the command line has the form `var=val', it is treated as a variable assignment: the variable var is assigned the value val. If any of the files have a value that is the null string, that element in the list is skipped.
For each record in the input, gawk tests to see if it matches any
pattern in the awk program. For each pattern that the record
matches, the associated action is executed.
awk variables are not declared; they come into existence when they are
first used. Their values are either floating-point numbers or strings.
awk also has one-dimensional arrays; multiple-dimensional arrays
may be simulated. There are several predefined variables that
awk sets as a program runs; these are summarized below.
As each input line is read, gawk splits the line into
fields, using the value of the FS variable as the field
separator. If FS is a single character, fields are separated by
that character. Otherwise, FS is expected to be a full regular
expression. In the special case that FS is a single space,
fields are separated by runs of spaces, tabs and/or newlines.(29)
If FS is the null string (""), then each individual
character in the record becomes a separate field.
Note that the value
of IGNORECASE (see section Kis- és nagybetűk az illesztésekben)
also affects how fields are split when FS is a regular expression.
Each field in the input line may be referenced by its position, $1,
$2, and so on. $0 is the whole line. The value of a field may
be assigned to as well. Field numbers need not be constants:
n = 5 print $n
prints the fifth field in the input line. The variable NF is set to
the total number of fields in the input line.
References to non-existent fields (i.e. fields after $NF) return
the null string. However, assigning to a non-existent field (e.g.,
$(NF+2) = 5) increases the value of NF, creates any
intervening fields with the null string as their value, and causes the
value of $0 to be recomputed, with the fields being separated by
the value of OFS.
Decrementing NF causes the values of fields past the new value to
be lost, and the value of $0 to be recomputed, with the fields being
separated by the value of OFS.
See section Bemeneti file-ok olvasása.
gawk's built-in variables are:
ARGC
ARGV. See below for what is actually
included in ARGV.
ARGIND
ARGV of the current file being processed.
When gawk is processing the input data files,
it is always true that `FILENAME == ARGV[ARGIND]'.
ARGV
ARGC - 1. Dynamically changing ARGC and
the contents of ARGV
can control the files used for data. A null-valued element in
ARGV is ignored. ARGV does not include the options to
awk or the text of the awk program itself.
CONVFMT
FIELDWIDTHS
ENVIRON
HOME is
ENVIRON["HOME"]. One possible value might be `/home/arnold'.
Changing this array does not affect the environment seen by programs
which gawk spawns via redirection or the system function.
(This may change in a future version of gawk.)
Some operating systems do not have environment variables.
The ENVIRON array is empty when running on these systems.
ERRNO
getline
or close.
FILENAME
FILENAME is the null string.
FNR
FS
IGNORECASE
IGNORECASE has a non-zero value, then pattern
matching in rules, record separating with RS, field splitting
with FS, regular expression matching with `~' and
`!~', and the gensub, gsub, index,
match, split and sub built-in functions all
ignore case when doing regular expression operations, and all string
comparisons are done ignoring case.
The value of IGNORECASE does not affect array subscripting.
NF
NR
OFMT
print statement,
"%.6g" by default.
OFS
ORS
RS
RS is set to the null string, then records are separated by
blank lines. When RS is set to the null string, then the newline
character always acts as a field separator, in addition to whatever value
FS may have. If RS is set to a multi-character
string, it denotes a regexp; input text matching the regexp
separates records.
RT
RS,
the record separator.
RSTART
match; zero if no match.
RLENGTH
match; -1 if no match.
SUBSEP
"\034".
See section Beépített változók, for more information.
Arrays are subscripted with an expression between square brackets (`[' and `]'). Array subscripts are always strings; numbers are converted to strings as necessary, following the standard conversion rules (see section Szövegek és számok konverziója).
If you use multiple expressions separated by commas inside the square
brackets, then the array subscript is a string consisting of the
concatenation of the individual subscript values, converted to strings,
separated by the subscript separator (the value of SUBSEP).
The special operator in may be used in a conditional context
to see if an array has an index consisting of a particular value.
if (val in array)
print array[val]
If the array has multiple subscripts, use `(i, j, ...) in array' to test for existence of an element.
The in construct may also be used in a for loop to iterate
over all the elements of an array.
See section Egy tömb elemeinek ellenőrzése.
You can remove an element from an array using the delete statement.
You can clear an entire array using `delete array'.
See section Tömbök az awk-ban.
The value of an awk expression is always either a number
or a string.
Some contexts (such as arithmetic operators) require numeric values. They convert strings to numbers by interpreting the text of the string as a number. If the string does not look like a number, it converts to zero.
Other contexts (such as concatenation) require string values.
They convert numbers to strings by effectively printing them
with sprintf.
See section Szövegek és számok konverziója, for the details.
To force conversion of a string value to a number, simply add zero to it. If the value you start with is already a number, this does not change it.
To force conversion of a numeric value to a string, concatenate it with the null string.
Comparisons are done numerically if both operands are numeric, or if
one is numeric and the other is a numeric string. Otherwise one or
both operands are converted to strings and a string comparison is
performed. Fields, getline input, FILENAME, ARGV
elements, ENVIRON elements and the elements of an array created
by split are the only items that can be numeric strings. String
constants, such as "3.1415927" are not numeric strings, they are
string constants. The full rules for comparisons are described in
section Változó típusok és az összehasonlító kifejezések.
Uninitialized variables have the string value "" (the null, or
empty, string). In contexts where a number is required, this is
equivalent to zero.
See section Változók, for more information on variable naming and initialization; see section Szövegek és számok konverziója, for more information on how variable values are interpreted.
An awk program is mostly composed of rules, each consisting of a
pattern followed by an action. The action is enclosed in `{' and
`}'. Either the pattern may be missing, or the action may be
missing, but not both. If the pattern is missing, the
action is executed for every input record. A missing action is
equivalent to `{ print }', which prints the entire line.
Comments begin with the `#' character, and continue until the end of the
line. Blank lines may be used to separate statements. Statements normally
end with a newline; however, this is not the case for lines ending in a
`,', `{', `?', `:', `&&', or `||'. Lines
ending in do or else also have their statements automatically
continued on the following line. In other cases, a line can be continued by
ending it with a `\', in which case the newline is ignored.
Multiple statements may be put on one line by separating each one with a `;'. This applies to both the statements within the action part of a rule (the usual case), and to the rule statements.
See section Megjegyzések az awk programokban, for information on
awk's commenting convention;
see section awk kifejezések és sorok, for a
description of the line continuation mechanism in awk.
awk patterns may be one of the following:
/regular expression/ relational expression pattern && pattern pattern || pattern pattern ? pattern : pattern (pattern) ! pattern pattern1, pattern2 BEGIN END
BEGIN and END are two special kinds of patterns that are not
tested against the input. The action parts of all BEGIN rules are
concatenated as if all the statements had been written in a single BEGIN
rule. They are executed before any of the input is read. Similarly, all the
END rules are concatenated, and executed when all the input is exhausted (or
when an exit statement is executed). BEGIN and END
patterns cannot be combined with other patterns in pattern expressions.
BEGIN and END rules cannot have missing action parts.
For /regular-expression/ patterns, the associated statement is
executed for each input record that matches the regular expression. Regular
expressions are summarized below.
A relational expression may use any of the operators defined below in the section on actions. These generally test whether certain fields match certain regular expressions.
The `&&', `||', and `!' operators are logical "and," logical "or," and logical "not," respectively, as in C. They do short-circuit evaluation, also as in C, and are used for combining more primitive pattern expressions. As in most languages, parentheses may be used to change the order of evaluation.
The `?:' operator is like the same operator in C. If the first pattern matches, then the second pattern is matched against the input record; otherwise, the third is matched. Only one of the second and third patterns is matched.
The `pattern1, pattern2' form of a pattern is called a range pattern. It matches all input lines starting with a line that matches pattern1, and continuing until a line that matches pattern2, inclusive. A range pattern cannot be used as an operand of any of the pattern operators.
See section Minta elemek.
Regular expressions are based on POSIX EREs (extended regular expressions). The escape sequences allowed in string constants are also valid in regular expressions (see section Escape szekvenciák). Regexps are composed of characters as follows:
c
\c
.
^
$
[abc...]
[[:class:]]
alnum, alpha, blank, cntrl,
digit, graph, lower, print, punct,
space, upper, and xdigit.
[[.symbol.]]
gawk does not currently support collating symbols.
[[=classname=]]
gawk does not currently support equivalence classes.
[^abc...]
r1|r2
r1r2
r+
r*
r?
(r)
r{n}
r{n,}
r{n,m}
\y
\B
\<
\>
\w
\W
\`
gawk).
\'
The various command line options
control how gawk interprets characters in regexps.
gawk provide all the facilities of
POSIX regexps and the GNU regexp operators described above.
However, interval expressions are not supported.
--posix
--traditional
awk regexps are matched. The GNU operators
are not special, interval expressions are not available, and neither
are the POSIX character classes ([[:alnum:]] and so on).
Characters described by octal and hexadecimal escape sequences are
treated literally, even if they represent regexp metacharacters.
--re-interval
See section Reguláris kifejezések.
Action statements are enclosed in braces, `{' and `}'. A missing action statement is equivalent to `{ print }'.
Action statements consist of the usual assignment, conditional, and looping statements found in most languages. The operators, control statements, and Input/Output statements available are similar to those in C.
Comments begin with the `#' character, and continue until the end of the
line. Blank lines may be used to separate statements. Statements normally
end with a newline; however, this is not the case for lines ending in a
`,', `{', `?', `:', `&&', or `||'. Lines
ending in do or else also have their statements automatically
continued on the following line. In other cases, a line can be continued by
ending it with a `\', in which case the newline is ignored.
Multiple statements may be put on one line by separating each one with a `;'. This applies to both the statements within the action part of a rule (the usual case), and to the rule statements.
See section Megjegyzések az awk programokban, for information on
awk's commenting convention;
see section awk kifejezések és sorok, for a
description of the line continuation mechanism in awk.
The operators in awk, in order of decreasing precedence, are:
(...)
$
++ --
^
+ - !
* / %
+ -
space
< <= > >= != ==
~ !~
in
&&
||
?:
= += -= *= /= %= ^=
var=value)
and operator assignment (the other forms) are supported.
See section Kifejezések.
The control statements are as follows:
if (condition) statement [ else statement ]
while (condition) statement
do statement while (condition)
for (expr1; expr2; expr3) statement
for (var in array) statement
break
continue
delete array[index]
delete array
exit [ expression ]
{ statements }
See section Vezérlésátadó kifejezések a tevékenységekben.
The Input/Output statements are as follows:
getline
$0 from next input record; set NF, NR, FNR.
See section Explicit beolvasás getline-al.
getline <file
$0 from next record of file; set NF.
getline var
NR, FNR.
getline var <file
command | getline
getline; sets $0,
NF, NR.
command | getline var
getline; sets var.
next
awk program.
If the end of the input data is reached, the END rule(s), if any,
are executed.
See section A next kifejezés.
nextfile
FILENAME is updated, FNR is set to one,
ARGIND is incremented,
and processing starts over with the first pattern in the awk program.
If the end of the input data is reached, the END rule(s), if any,
are executed.
Earlier versions of gawk used `next file'; this usage is still
supported, but is considered to be deprecated.
See section A nextfile kifejezés.
print
print expr-list
print expr-list > file
print is executed.
print expr-list >> file
print is appended to the file.
print expr-list | command
close function
is called.
printf fmt, expr-list
printf fmt, expr-list > file
printf is executed.
printf fmt, expr-list >> file
printf is appended to the file.
printf fmt, expr-list | command
close function
is called.
getline returns zero on end of file, and -1 on an error.
In the event of an error, getline will set ERRNO to
the value of a system-dependent string that describes the error.
printf Summary
Conversion specification have the form
%[flag][width][.prec]format.
Items in brackets are optional.
The awk printf statement and sprintf function
accept the following conversion specification formats:
%c
%d
%i
%e
%E
%f
-]ddd.dddddd.
%g
%G
%o
%s
%x
%X
%%
There are optional, additional parameters that may lie between the `%' and the control letter:
-
space
+
#
0
width
.prec
Either or both of the width and prec values may be specified as `*'. In that case, the particular value is taken from the argument list.
See section Nyomtatás a printf kifejezéssel.
When doing I/O redirection from either print or printf into a
file, or via getline from a file, gawk recognizes certain special
file names internally. These file names allow access to open file descriptors
inherited from gawk's parent process (usually the shell). The
file names are:
In addition, reading the following files provides process related information
about the running gawk program. All returned records are terminated
with a newline.
getuid, geteuid, getgid, and getegid
system calls.
If there are any additional fields, they are the group IDs returned by
getgroups system call.
(Multiple groups may not be supported on all systems.)
These file names may also be used on the command line to name data files. These file names are only recognized internally if you do not actually have files with these names on your system.
See section Speciális file nevek gawk-ban, for a longer description that
provides the motivation for this feature.
awk provides a number of built-in functions for performing
numeric operations, string related operations, and I/O related operations.
The built-in arithmetic functions are:
atan2(y, x)
cos(expr)
exp(expr)
e ^ expr).
int(expr)
log(expr)
expr.
rand()
sin(expr)
sqrt(expr)
srand([expr])
awk has the following built-in string functions:
gensub(regex, subst, how [, target])
$0. The return value is the changed string; the
original target is not modified. Within subst,
`\n', where n is a digit from one to nine, can be used to
indicate the text that matched the n'th parenthesized
subexpression.
This function is gawk-specific.
gsub(regex, subst [, target])
$0.
index(str, search)
length([str])
$0
is returned if no argument is supplied.
match(str, regex)
RSTART and RLENGTH.
split(str, arr [, regex])
FS is used instead. regex can be the null string, causing
each character to be placed into its own array element.
The array arr is cleared first.
sprintf(fmt, expr-list)
sub(regex, subst [, target])
gsub, but only the first matching substring is replaced.
substr(str, index [, len])
tolower(str)
toupper(str)
The I/O related functions are:
close(expr)
fflush([expr])
""), all output buffers are flushed.
system(cmd-line)
system, calling it will
generate a fatal error.
`system("")' can be used to force awk to flush any pending
output. This is more portable, but less obvious, than calling fflush.
The following two functions are available for getting the current
time of day, and for formatting time stamps.
They are specific to gawk.
systime()
strftime([format[, timestamp]])
date utility is used if
no format is supplied.
See section Dátumfeldolgozó függvények, for the
details on the conversion specifiers that strftime accepts.
See section Beépített függvények, for a description of all of
awk's built-in functions.
String constants in awk are sequences of characters enclosed
in double quotes ("). Within strings, certain escape sequences
are recognized, as in C. These are:
\\
\a
\b
\f
\n
\r
\t
\v
\xhex digits
"\x1B" is a
string containing the ASCII ESC (escape) character. (The `\x'
escape sequence is not in POSIX awk.)
\ddd
"\033" is also a string containing the ASCII ESC
(escape) character.
\c
The escape sequences may also be used inside constant regular expressions
(e.g., the regexp /[ \t\f\n\r\v]/ matches whitespace
characters).
See section Escape szekvenciák.
Functions in awk are defined as follows:
function name(parameter list) { statements }
Actual parameters supplied in the function call are used to instantiate the formal parameters declared in the function. Arrays are passed by reference, other variables are passed by value.
If there are fewer arguments passed than there are names in parameter-list, the extra names are given the null string as their value. Extra names have the effect of local variables.
The open-parenthesis in a function call of a user-defined function must immediately follow the function name, without any intervening white space. This is to avoid a syntactic ambiguity with the concatenation operator.
The word func may be used in place of function (but not in
POSIX awk).
Use the return statement to return a value from a function.
See section Felhasználó által definiált függvények.
There are two features of historical awk implementations that
gawk supports.
First, it is possible to call the length built-in function not only
with no arguments, but even without parentheses!
a = length
is the same as either of
a = length() a = length($0)
For example:
$ echo abcdef | awk '{ print length }'
-| 6
This feature is marked as "deprecated" in the POSIX standard, and
gawk will issue a warning about its use if `--lint' is
specified on the command line.
(The ability to use length this way was actually an accident of the
original Unix awk implementation. If any built-in function used
$0 as its default argument, it was possible to call that function
without the parentheses. In particular, it was common practice to use
the length function in this fashion, and this usage was documented
in the awk manual page.)
The other historical feature is the use of either the break statement,
or the continue statement
outside the body of a while, for, or do loop. Traditional
awk implementations have treated such usage as equivalent to the
next statement. More recent versions of Unix awk do not allow
it. gawk supports this usage if `--traditional' has been
specified.
See section Command Line Options, for more information about the `--posix' and `--lint' options.
gawk
This appendix provides instructions for installing gawk on the
various platforms that are supported by the developers. The primary
developers support Unix (and one day, GNU), while the other ports were
contributed. The file `ACKNOWLEDGMENT' in the gawk
distribution lists the electronic mail addresses of the people who did
the respective ports, and they are also provided in
section Reporting Problems and Bugs.
gawk Distribution
This section first describes how to get the gawk
distribution, how to extract it, and then what is in the various files and
subdirectories.
gawk DistributionThere are three ways you can get GNU software.
gawk directly from the Free Software Foundation.
Software distributions are available for Unix, MS-DOS, and VMS, on
tape and CD-ROM. The address is:
Ordering from the FSF directly contributes to the support of the foundation and to the production of more free software.Free Software Foundation
59 Temple Place--Suite 330
Boston, MA 02111-1307 USA
Phone: +1-617-542-5942
Fax (including Japan): +1-617-542-2652
E-mail:gnu@gnu.org
gawk by using anonymous ftp to the Internet host
gnudist.gnu.org, in the directory `/gnu/gawk'.
Here is a list of alternate ftp sites from which you can obtain GNU
software. When a site is listed as "site:directory" the
directory indicates the directory where GNU software is kept.
You should use a site that is geographically close to you.
cair-archive.kaist.ac.kr:/pub/gnu
ftp.cs.titech.ac.jp
ftp.nectec.or.th:/pub/mirrors/gnu
utsun.s.u-tokyo.ac.jp:/ftpsync/prep
archie.au:/gnu
archie.oz or archie.oz.au for ACSnet)
ftp.sun.ac.za:/pub/gnu
ftp.technion.ac.il:/pub/unsupported/gnu
archive.eu.net
ftp.denet.dk
ftp.eunet.ch
ftp.funet.fi:/pub/gnu
ftp.ieunet.ie:pub/gnu
ftp.informatik.rwth-aachen.de:/pub/gnu
ftp.informatik.tu-muenchen.de
ftp.luth.se:/pub/unix/gnu
ftp.mcc.ac.uk
ftp.stacken.kth.se
ftp.sunet.se:/pub/gnu
ftp.univ-lyon1.fr:pub/gnu
ftp.win.tue.nl:/pub/gnu
irisa.irisa.fr:/pub/gnu
isy.liu.se
nic.switch.ch:/mirror/gnu
src.doc.ic.ac.uk:/gnu
unix.hensa.ac.uk:/pub/uunet/systems/gnu
ftp.inf.utfsm.cl:/pub/gnu
ftp.unicamp.br:/pub/gnu
ftp.cs.ubc.ca:/mirror2/gnu
col.hp.com:/mirrors/gnu
f.ms.uky.edu:/pub3/gnu
ftp.cc.gatech.edu:/pub/gnu
ftp.cs.columbia.edu:/archives/gnu/prep
ftp.digex.net:/pub/gnu
ftp.hawaii.edu:/mirrors/gnu
ftp.kpc.com:/pub/mirror/gnu
ftp.uu.net:/systems/gnu
gatekeeper.dec.com:/pub/GNU
jaguar.utah.edu:/gnustuff
labrea.stanford.edu
mrcnext.cso.uiuc.edu:/pub/gnu
vixen.cso.uiuc.edu:/gnu
wuarchive.wustl.edu:/systems/gnu
gawk is distributed as a tar file compressed with the
GNU Zip program, gzip.
Once you have the distribution (for example,
`gawk-3.0.4.tar.gz'), first use gzip to expand the
file, and then use tar to extract it. You can use the following
pipeline to produce the gawk distribution:
# Under System V, add 'o' to the tar flags gzip -d -c gawk-3.0.4.tar.gz | tar -xvpf -
This will create a directory named `gawk-3.0.4' in the current directory.
The distribution file name is of the form
`gawk-V.R.n.tar.gz'.
The V represents the major version of gawk,
the R represents the current release of version V, and
the n represents a patch level, meaning that minor bugs have
been fixed in the release. The current patch level is 4,
but when
retrieving distributions, you should get the version with the highest
version, release, and patch level. (Note that release levels greater than
or equal to 90 denote "beta," or non-production software; you may not wish
to retrieve such a version unless you don't mind experimenting.)
If you are not on a Unix system, you will need to make other arrangements
for getting and extracting the gawk distribution. You should consult
a local expert.
gawk Distribution
The gawk distribution has a number of C source files,
documentation files,
subdirectories and files related to the configuration process
(see section Compiling and Installing gawk on Unix),
and several subdirectories related to different, non-Unix,
operating systems.
gawk source code.
gawk under Unix, and the
rest for the various hardware and software combinations.
gawk has been ported, and which
have successfully run the test suite.
gawk since the last release or patch.
gawk's performance.
Most of these depend on the hardware or operating system software, and
are not limits in gawk itself.
awk is
incorrect, and how gawk handles the problem.
gawk is a good language for
AI (Artificial Intelligence) programming.
troff source for a five-color awk reference card.
A modern version of troff, such as GNU Troff (groff) is
needed to produce the color version. See the file `README.card'
for instructions if you have an older troff.
troff source for a manual page describing gawk.
This is distributed for the convenience of Unix users.
makeinfo to produce an Info file.
troff source for a manual page describing the igawk
program presented in
section An Easy Way to Use Library Functions.
gawk
for various Unix systems. They are explained in detail in
section Compiling and Installing gawk on Unix.
configure uses to generate a `Makefile'.
As part of the process of building gawk, the library functions from
section A Library of awk Functions,
and the igawk program from
section An Easy Way to Use Library Functions,
are extracted into ready to use files.
They are installed as part of the installation process.
gawk on an Atari ST.
See section Installing gawk on the Atari ST, for details.
gawk under MS-DOS and OS/2.
See section MS-DOS and OS/2 Installation and Compilation, for details.
gawk under VMS.
See section How to Compile and Install gawk on VMS, for details.
gawk. You can use `make check' from the top level gawk
directory to run your version of gawk against the test suite.
If gawk successfully passes `make check' then you can
be confident of a successful port.
gawk on Unix
Usually, you can compile and install gawk by typing only two
commands. However, if you do use an unusual system, you may need
to configure gawk for your system yourself.
gawk for Unix
After you have extracted the gawk distribution, cd
to `gawk-3.0.4'. Like most GNU software,
gawk is configured
automatically for your Unix system by running the configure program.
This program is a Bourne shell script that was generated automatically using
GNU autoconf.
(The autoconf software is
described fully in
Autoconf--Generating Automatic Configuration Scripts,
which is available from the Free Software Foundation.)
To configure gawk, simply run configure:
sh ./configure
This produces a `Makefile' and `config.h' tailored to your system.
The `config.h' file describes various facts about your system.
You may wish to edit the `Makefile' to
change the CFLAGS variable, which controls
the command line options that are passed to the C compiler (such as
optimization levels, or compiling for debugging).
Alternatively, you can add your own values for most make
variables, such as CC and CFLAGS, on the command line when
running configure:
CC=cc CFLAGS=-g sh ./configure
See the file `INSTALL' in the gawk distribution for
all the details.
After you have run configure, and possibly edited the `Makefile',
type:
make
and shortly thereafter, you should have an executable version of gawk.
That's all there is to it!
(If these steps do not work, please send in a bug report;
see section Reporting Problems and Bugs.)
(This section is of interest only if you know something about using the C language and the Unix operating system.)
The source code for gawk generally attempts to adhere to formal
standards wherever possible. This means that gawk uses library
routines that are specified by the ANSI C standard and by the POSIX
operating system interface standard. When using an ANSI C compiler,
function prototypes are used to help improve the compile-time checking.
Many Unix systems do not support all of either the ANSI or the
POSIX standards. The `missing' subdirectory in the gawk
distribution contains replacement versions of those subroutines that are
most likely to be missing.
The `config.h' file that is created by the configure program
contains definitions that describe features of the particular operating
system where you are attempting to compile gawk. The three things
described by this file are what header files are available, so that
they can be correctly included,
what (supposedly) standard functions are actually available in your C
libraries, and
other miscellaneous facts about your
variant of Unix. For example, there may not be an st_blksize
element in the stat structure. In this case `HAVE_ST_BLKSIZE'
would be undefined.
It is possible for your C compiler to lie to configure. It may
do so by not exiting with an error when a library function is not
available. To get around this, you can edit the file `custom.h'.
Use an `#ifdef' that is appropriate for your system, and either
#define any constants that configure should have defined but
didn't, or #undef any constants that configure defined and
should not have. `custom.h' is automatically included by
`config.h'.
It is also possible that the configure program generated by
autoconf
will not work on your system in some other fashion. If you do have a problem,
the file
`configure.in' is the input for autoconf. You may be able to
change this file, and generate a new version of configure that will
work on your system. See section Reporting Problems and Bugs, for
information on how to report problems in configuring gawk. The same
mechanism may be used to send in updates to `configure.in' and/or
`custom.h'.
gawk on VMS
This section describes how to compile and install gawk under VMS.
gawk on VMS
To compile gawk under VMS, there is a DCL command procedure that
will issue all the necessary CC and LINK commands, and there is
also a `Makefile' for use with the MMS utility. From the source
directory, use either
$ @[.VMS]VMSBUILD.COM
or
$ MMS/DESCRIPTION=[.VMS]DESCRIP.MMS GAWK
Depending upon which C compiler you are using, follow one of the sets of instructions in this table:
CC/OPTIMIZE=NOLINE, which is essential for Version 3.0.
gawk has been tested under VAX/VMS 5.5-1 using VAX C V3.2,
GNU C 1.40 and 2.3. It should work without modifications for VMS V4.6 and up.
gawk on VMS
To install gawk, all you need is a "foreign" command, which is
a DCL symbol whose value begins with a dollar sign. For example:
$ GAWK :== $disk1:[gnubin]GAWK
(Substitute the actual location of gawk.exe for
`$disk1:[gnubin]'.) The symbol should be placed in the
`login.com' of any user who wishes to run gawk,
so that it will be defined every time the user logs on.
Alternatively, the symbol may be placed in the system-wide
`sylogin.com' procedure, which will allow all users
to run gawk.
Optionally, the help entry can be loaded into a VMS help library:
$ LIBRARY/HELP SYS$HELP:HELPLIB [.VMS]GAWK.HLP
(You may want to substitute a site-specific help library rather than the standard VMS library `HELPLIB'.) After loading the help text,
$ HELP GAWK
will provide information about both the gawk implementation and the
awk programming language.
The logical name `AWK_LIBRARY' can designate a default location
for awk program files. For the `-f' option, if the specified
filename has no device or directory path information in it, gawk
will look in the current directory first, then in the directory specified
by the translation of `AWK_LIBRARY' if the file was not found.
If after searching in both directories, the file still is not found,
then gawk appends the suffix `.awk' to the filename and the
file search will be re-tried. If `AWK_LIBRARY' is not defined, that
portion of the file search will fail benignly.
gawk on VMS
Command line parsing and quoting conventions are significantly different
on VMS, so examples in this könyv or from other sources often need minor
changes. They are minor though, and all awk programs
should run correctly.
Here are a couple of trivial tests:
$ gawk -- "BEGIN {print ""Hello, World!""}"
$ gawk -"W" version
! could also be -"W version" or "-W version"
Note that upper-case and mixed-case text must be quoted.
The VMS port of gawk includes a DCL-style interface in addition
to the original shell-style interface (see the help entry for details).
One side-effect of dual command line parsing is that if there is only a
single parameter (as in the quoted string program above), the command
becomes ambiguous. To work around this, the normally optional `--'
flag is required to force Unix style rather than DCL parsing. If any
other dash-type options (or multiple parameters such as data files to be
processed) are present, there is no ambiguity and `--' can be omitted.
The default search path when looking for awk program files specified
by the `-f' option is "SYS$DISK:[],AWK_LIBRARY:". The logical
name `AWKPATH' can be used to override this default. The format
of `AWKPATH' is a comma-separated list of directory specifications.
When defining it, the value should be quoted so that it retains a single
translation, and not a multi-translation RMS searchlist.
gawk on VMS POSIXIgnore the instructions above, although `vms/gawk.hlp' should still be made available in a help library. The source tree should be unpacked into a container file subsystem rather than into the ordinary VMS file system. Make sure that the two scripts, `configure' and `vms/posix-cc.sh', are executable; use `chmod +x' on them if necessary. Then execute the following two commands:
psx> CC=vms/posix-cc.sh configure psx> make CC=c89 gawk
The first command will construct files `config.h' and `Makefile' out
of templates, using a script to make the C compiler fit configure's
expectations. The second command will compile and link gawk using
the C compiler directly; ignore any warnings from make about being
unable to redefine CC. configure will take a very long
time to execute, but at least it provides incremental feedback as it
runs.
This has been tested with VAX/VMS V6.2, VMS POSIX V2.0, and DEC C V5.2.
Once built, gawk will work like any other shell utility. Unlike
the normal VMS port of gawk, no special command line manipulation is
needed in the VMS POSIX environment.
If you have received a binary distribution prepared by the DOS
maintainers, then gawk and the necessary support files will appear
under the `gnu' directory, with executables in `gnu/bin',
libraries in `gnu/lib/awk', and manual pages under `gnu/man'.
This is designed for easy installation to a `/gnu' directory on your
drive, but the files can be installed anywhere provided AWKPATH is
set properly. Regardless of the installation directory, the first line of
`igawk.cmd' and `igawk.bat' (in `gnu/bin') may need to be
edited.
The binary distribution will contain a separate file describing the
contents. In particular, it may include more than one version of the
gawk executable. OS/2 binary distributions may have a
different arrangement, but installation is similar.
The OS/2 and MS-DOS versions of gawk search for program files as
described in section The AWKPATH Environment Variable.
However, semicolons (rather than colons) separate elements
in the AWKPATH variable. If AWKPATH is not set or is empty,
then the default search path is ".;c:/lib/awk;c:/gnu/lib/awk".
An sh-like shell (as opposed to command.com under MS-DOS
or cmd.exe under OS/2) may be useful for awk programming.
Ian Stewartson has written an excellent shell for MS-DOS and OS/2, and a
ksh clone and GNU Bash are available for OS/2. The file
`README_d/README.pc' in the gawk distribution contains
information on these shells. Users of Stewartson's shell on DOS should
examine its documentation on handling of command-lines. In particular,
the setting for gawk in the shell configuration may need to be
changed, and the ignoretype option may also be of interest.
gawk can be compiled for MS-DOS and OS/2 using the GNU development tools
from DJ Delorie (DJGPP, MS-DOS-only) or Eberhard Mattes (EMX, MS-DOS and OS/2).
Microsoft C can be used to build 16-bit versions for MS-DOS and OS/2. The file
`README_d/README.pc' in the gawk distribution contains additional
notes, and `pc/Makefile' contains important notes on compilation options.
To build gawk, copy the files in the `pc' directory (except
for `ChangeLog') to the
directory with the rest of the gawk sources. The `Makefile'
contains a configuration section with comments, and may need to be
edited in order to work with your make utility.
The `Makefile' contains a number of targets for building various MS-DOS
and OS/2 versions. A list of targets will be printed if the make
command is given without a target. As an example, to build gawk
using the DJGPP tools, enter `make djgpp'.
Using make to run the standard tests and to install gawk
requires additional Unix-like tools, including sh, sed, and
cp. In order to run the tests, the `test/*.ok' files may need to
be converted so that they have the usual DOS-style end-of-line markers. Most
of the tests will work properly with Stewartson's shell along with the
companion utilities or appropriate GNU utilities. However, some editing of
`test/Makefile' is required. It is recommended that the file
`pc/Makefile.tst' be copied to `test/Makefile' as a
replacement. Details can be found in `README_d/README.pc'.
gawk on the Atari ST
There are no substantial differences when installing gawk on
various Atari models. Compiled gawk executables do not require
a large amount of memory with most awk programs and should run on all
Motorola processor based models (called further ST, even if that is not
exactly right).
In order to use gawk, you need to have a shell, either text or
graphics, that does not map all the characters of a command line to
upper-case. Maintaining case distinction in option flags is very
important (see section Command Line Options).
These days this is the default, and it may only be a problem for some
very old machines. If your system does not preserve the case of option
flags, you will need to upgrade your tools. Support for I/O
redirection is necessary to make it easy to import awk programs
from other environments. Pipes are nice to have, but not vital.
gawk on the Atari ST
A proper compilation of gawk sources when sizeof(int)
differs from sizeof(void *) requires an ANSI C compiler. An initial
port was done with gcc. You may actually prefer executables
where ints are four bytes wide, but the other variant works as well.
You may need quite a bit of memory when trying to recompile the gawk
sources, as some source files (`regex.c' in particular) are quite
big. If you run out of memory compiling such a file, try reducing the
optimization level for this particular file; this may help.
With a reasonable shell (Bash will do), and in particular if you run
Linux, MiNT or a similar operating system, you have a pretty good
chance that the configure utility will succeed. Otherwise
sample versions of `config.h' and `Makefile.st' are given in the
`atari' subdirectory and can be edited and copied to the
corresponding files in the main source directory. Even if
configure produced something, it might be advisable to compare
its results with the sample versions and possibly make adjustments.
Some gawk source code fragments depend on a preprocessor define
`atarist'. This basically assumes the TOS environment with gcc.
Modify these sections as appropriate if they are not right for your
environment. Also see the remarks about AWKPATH and envsep in
section Running gawk on the Atari ST.
As shipped, the sample `config.h' claims that the system
function is missing from the libraries, which is not true, and an
alternative implementation of this function is provided in
`atari/system.c'. Depending upon your particular combination of
shell and operating system, you may wish to change the file to indicate
that system is available.
gawk on the Atari ST
An executable version of gawk should be placed, as usual,
anywhere in your PATH where your shell can find it.
While executing, gawk creates a number of temporary files. When
using gcc libraries for TOS, gawk looks for either of
the environment variables TEMP or TMPDIR, in that order.
If either one is found, its value is assumed to be a directory for
temporary files. This directory must exist, and if you can spare the
memory, it is a good idea to put it on a RAM drive. If neither
TEMP nor TMPDIR are found, then gawk uses the
current directory for its temporary files.
The ST version of gawk searches for its program files as described in
section The AWKPATH Environment Variable.
The default value for the AWKPATH variable is taken from
DEFPATH defined in `Makefile'. The sample gcc/TOS
`Makefile' for the ST in the distribution sets DEFPATH to
".,c:\lib\awk,c:\gnu\lib\awk". The search path can be
modified by explicitly setting AWKPATH to whatever you wish.
Note that colons cannot be used on the ST to separate elements in the
AWKPATH variable, since they have another, reserved, meaning.
Instead, you must use a comma to separate elements in the path. When
recompiling, the separating character can be modified by initializing
the envsep variable in `atari/gawkmisc.atr' to another
value.
Although awk allows great flexibility in doing I/O redirections
from within a program, this facility should be used with care on the ST
running under TOS. In some circumstances the OS routines for file
handle pool processing lose track of certain events, causing the
computer to crash, and requiring a reboot. Often a warm reboot is
sufficient. Fortunately, this happens infrequently, and in rather
esoteric situations. In particular, avoid having one part of an
awk program using print statements explicitly redirected
to "/dev/stdout", while other print statements use the
default standard output, and a calling shell has redirected standard
output to a file.
When gawk is compiled with the ST version of gcc and its
usual libraries, it will accept both `/' and `\' as path separators.
While this is convenient, it should be remembered that this removes one,
technically valid, character (`/') from your file names, and that
it may create problems for external programs, called via the system
function, which may not support this convention. Whenever it is possible
that a file created by gawk will be used by some other program,
use only backslashes. Also remember that in awk, backslashes in
strings have to be doubled in order to get literal backslashes
(see section Escape szekvenciák).
gawk on an Amiga
You can install gawk on an Amiga system using a Unix emulation
environment available via anonymous ftp from
ftp.ninemoons.com in the directory `pub/ade/current'.
This includes a shell based on pdksh. The primary component of
this environment is a Unix emulation library, `ixemul.lib'.
A more complete distribution for the Amiga is available on the Geek Gadgets CD-ROM from:
CRONUS
1840 E. Warner Road #105-265
Tempe, AZ 85284 USA
US Toll Free: (800) 804-0833
Phone: +1-602-491-0442
FAX: +1-602-491-0048
Email:info@ninemoons.com
WWW:http://www.ninemoons.com
Anonymousftpsite:ftp.ninemoons.com
Once you have the distribution, you can configure gawk simply by
running configure:
configure -v m68k-amigaos
Then run make, and you should be all set!
(If these steps do not work, please send in a bug report;
see section Reporting Problems and Bugs.)
There is nothing more dangerous than a bored archeologist. The Hitchhiker's Guide to the Galaxy
If you have problems with gawk or think that you have found a bug,
please report it to the developers; we cannot promise to do anything
but we might well want to fix it.
Before reporting a bug, make sure you have actually found a real bug. Carefully reread the documentation and see if it really says you can do what you're trying to do. If it's not clear whether you should be able to do something or not, report that too; it's a bug in the documentation!
Before reporting a bug or trying to fix it yourself, try to isolate it
to the smallest possible awk program and input data file that
reproduces the problem. Then send us the program and data file,
some idea of what kind of Unix system you're using, and the exact results
gawk gave you. Also say what you expected to occur; this will help
us decide whether the problem was really in the documentation.
Once you have a precise problem, there are two e-mail addresses you can send mail to.
Please include the
version number of gawk you are using. You can get this information
with the command `gawk --version'.
You should send a carbon copy of your mail to Arnold Robbins, who can
be reached at `arnold@gnu.org'.
Important! Do not try to report bugs in gawk by
posting to the Usenet/Internet newsgroup comp.lang.awk.
While the gawk developers do occasionally read this newsgroup,
there is no guarantee that we will see your posting. The steps described
above are the official, recognized ways for reporting bugs.
Non-bug suggestions are always welcome as well. If you have questions about things that are unclear in the documentation or are just obscure features, ask Arnold Robbins; he will try to help you out, although he may not have the time to fix the problem. You can send him electronic mail at the Internet address above.
If you find bugs in one of the non-Unix ports of gawk, please send
an electronic mail message to the person who maintains that port. They
are listed below, and also in the `README' file in the gawk
distribution. Information in the `README' file should be considered
authoritative if it conflicts with this könyv.
The people maintaining the non-Unix ports of gawk are:
If your bug is also reproducible under Unix, please send copies of your report to the general GNU bug list, as well as to Arnold Robbins, at the addresses listed above.
awk Implementations
It's kind of fun to put comments like this in your awk code.
// Do C++ comments work? answer: yes! of course
Michael Brennan
There are two other freely available awk implementations.
This section briefly describes where to get them.
awk
awk freely available. You can get it via anonymous ftp
to the host netlib.bell-labs.com. Change directory to
`/netlib/research'. Use "binary" or "image" mode, and
retrieve `awk.bundle.gz'.
This is a shell archive that has been compressed with the GNU gzip
utility. It can be uncompressed with the gunzip utility.
You can also retrieve this version via the World Wide Web from
Brian Kernighan's home page.
This version requires an ANSI C compiler; GCC (the GNU C compiler)
works quite nicely.
mawk
awk,
called mawk. It is available under the GPL
(see section GNU GENERAL PUBLIC LICENSE),
just as gawk is.
You can get it via anonymous ftp to the host
ftp.whidbey.net. Change directory to `/pub/brennan'.
Use "binary" or "image" mode, and retrieve `mawk1.3.3.tar.gz'
(or the latest version that is there).
gunzip may be used to decompress this file. Installation
is similar to gawk's
(see section Compiling and Installing gawk on Unix).
This appendix contains information mainly of interest to implementors and
maintainers of gawk. Everything in it applies specifically to
gawk, and not to other implementations.
See section Extensions in gawk Not in POSIX awk,
for a summary of the GNU extensions to the awk language and program.
All of these features can be turned off by invoking gawk with the
`--traditional' option, or with the `--posix' option.
If gawk is compiled for debugging with `-DDEBUG', then there
is one more option available on the command line:
-W parsedebug
--parsedebug
This option is intended only for serious gawk developers,
and not for the casual user. It probably has not even been compiled into
your version of gawk, since it slows down execution.
gawk
If you should find that you wish to enhance gawk in a significant
fashion, you are perfectly free to do so. That is the point of having
free software; the source code is available, and you are free to change
it as you wish (see section GNU GENERAL PUBLIC LICENSE).
This section discusses the ways you might wish to change gawk,
and any considerations you should bear in mind.
You are free to add any new features you like to gawk.
However, if you want your changes to be incorporated into the gawk
distribution, there are several steps that you need to take in order to
make it possible for me to include your changes.
gawk. If your version of
gawk is very old, I may not be able to integrate them at all.
See section Getting the gawk Distribution,
for information on getting the latest version of gawk.
gawk.
(The GNU Coding Standards are available as part of the Autoconf
distribution, from the FSF.)
gawk coding style.
The C code for gawk follows the instructions in the
GNU Coding Standards, with minor exceptions. The code is formatted
using the traditional "K&R" style, particularly as regards the placement
of braces and the use of tabs. In brief, the coding rules for gawk
are:
int, on the
line above the line with the name and arguments of the function.
if, while, for, do, switch
and return).
for loop initialization and increment parts, and in macro bodies.
NULL and '\0' in the conditions of
if, while and for statements, and in the cases
of switch statements, instead of just the
plain pointer or character value.
TRUE, FALSE, and NULL symbolic constants,
and the character constant '\0' where appropriate, instead of 1
and 0.
alloca function for allocating memory off the stack.
Its use causes more portability trouble than the minor benefit of not having
to free the storage. Instead, use malloc and free.
gawk, I may not bother.
gnu@gnu.org.
gawk source tree with your version.
(I find context diffs to be more readable, but unified diffs are
more compact.)
I recommend using the GNU version of diff.
Send the output produced by either run of diff to me when you
submit your changes.
See section Reporting Problems and Bugs, for the electronic mail
information.
Using this format makes it easy for me to apply your changes to the
master version of the gawk source code (using patch).
If I have to apply the changes manually, using a text editor, I may
not do so, particularly if there are lots of changes.
Although this sounds like a lot of work, please remember that while you may write the new code, I have to maintain it and support it, and if it isn't possible for me to do that with a minimum of extra work, then I probably will not.
gawk to a New Operating System
If you wish to port gawk to a new operating system, there are
several steps to follow.
gawk, and the other ports. Avoid gratuitous
changes to the system-independent parts of the code. If at all possible,
avoid sprinkling `#ifdef's just for your port throughout the
code.
If the changes needed for a particular system affect too much of the
code, I probably will not accept them. In such a case, you will, of course,
be able to distribute your changes on your own, as long as you comply
with the GPL
(see section GNU GENERAL PUBLIC LICENSE).
gawk are maintained by other
people at the Free Software Foundation. Thus, you should not change them
unless it is for a very good reason. I.e. changes are not out of the
question, but changes to these files will be scrutinized extra carefully.
The files are `alloca.c', `getopt.h', `getopt.c',
`getopt1.c', `regex.h', `regex.c', `dfa.h',
`dfa.c', `install-sh', and `mkinstalldirs'.
gawk on their systems. If no-one
volunteers to maintain a port, that port becomes unsupported, and it may
be necessary to remove it from the distribution.
gawk for your system.
Following these steps will make it much easier to integrate your changes
into gawk, and have them co-exist happily with the code for other
operating systems that is already there.
In the code that you supply, and that you maintain, feel free to use a coding style and brace layout that suits your taste.
AWK is a language similar to PERL, only considerably more elegant. Arnold Robbins Hey! Larry Wall
This section briefly lists extensions and possible improvements
that indicate the directions we are
currently considering for gawk. The file `FUTURES' in the
gawk distributions lists these extensions as well.
This is a list of probable future changes that will be usable by the
awk language programmer.
gawk print its warnings and
error messages in languages other than English.
It may be possible for awk programs to also use the multiple
language facilities, separate from gawk itself.
awk array.
PROCINFO Array
gawk-ban)
may be superseded by a PROCINFO array that would provide the same
information, in an easier to access fashion.
lint warnings
gawk to the array ENVIRON may be
propagated to subprocesses run by gawk.
This is a list of probable improvements that will make gawk
perform better.
dfa
dfa pattern matcher from GNU grep has some
problems. Either a new version or a fixed one will deal with some
important regexp matching issues.
malloc
malloc could potentially speed up gawk,
since it relies heavily on the use of dynamic memory allocation.
Here are some projects that would-be gawk hackers might like to take
on. They vary in size from a few days to a few weeks of programming,
depending on which one you choose and how fast a programmer you are. Please
send any improvements you write to the maintainers at the GNU project.
See section Adding New Features,
for guidelines to follow when adding new features to gawk.
See section Reporting Problems and Bugs, for information on
contacting the maintainers.
awk programs: gawk uses a Bison (YACC-like)
parser to convert the script given it into a syntax tree; the syntax
tree is then executed by a simple recursive evaluator. This method incurs
a lot of overhead, since the recursive evaluator performs many procedure
calls to do even the simplest things.
It should be possible for gawk to convert the script's parse tree
into a C program which the user would then compile, using the normal
C compiler and a special gawk library to provide all the needed
functions (regexps, fields, associative arrays, type coercion, and so
on).
An easier possibility might be for an intermediate phase of awk to
convert the parse tree into a linear byte code form like the one used
in GNU Emacs Lisp. The recursive evaluator would then be replaced by
a straight line byte code interpreter that would be intermediate in speed
between running a compiled program and doing what gawk does
now.
awk statements attached to a rule. If the rule's
pattern matches an input record, awk executes the
rule's action. Actions are always enclosed in curly braces.
See section Tevékenységek áttekintése.
awk Assembler
awk scripts. It is thousands of lines long, including
machine descriptions for several eight-bit microcomputers.
It is a good example of a
program that would have been better written in another language.
awf)
awk and sh.
awk expression that changes the value of some awk
variable or data object. An object that you can assign to is called an
lvalue. The assigned values are called rvalues.
See section Értékadó kifejezések.
awk Language
awk programs are written.
awk Program
awk program consists of a series of patterns and
actions, collectively known as rules. For each input record
given to the program, the program's rules are all processed in turn.
awk programs may also contain function definitions.
awk Script
awk program.
ksh, pdksh, zsh) are
generally upwardly compatible with the Bourne shell.
awk language provides built-in functions that perform various
numerical, time stamp related, and string computations. Examples are
sqrt (for the square root of a number) and substr (for a
substring of a string). See section Beépített függvények.
ARGC, ARGIND, ARGV, CONVFMT, ENVIRON,
ERRNO, FIELDWIDTHS, FILENAME, FNR, FS,
IGNORECASE, NF, NR, OFMT, OFS, ORS,
RLENGTH, RSTART, RS, RT, and SUBSEP,
are the variables that have special meaning to awk.
Changing some of them affects awk's running environment.
Several of these variables are specific to gawk.
See section Beépített változók.
awk programming language has C-like syntax, and this könyv
points out similarities between awk and C when appropriate.
pic that reads descriptions of molecules
and produces pic input for drawing them. It was written in awk
by Brian Kernighan and Jon Bentley, and is available from
netlib@research.bell-labs.com.
awk statements, enclosed in curly braces. Compound
statements may be nested.
See section Vezérlésátadó kifejezések a tevékenységekben.
if, while, do,
and for
statements, and in patterns to select which input records to process.
See section Változó típusok és az összehasonlító kifejezések.
awk for delimiting actions, compound statements, and function
bodies.
awk stores numeric values. It is the C type double.
"foo", but it may also be an expression whose value can vary.
See section Dinamikus reguláris kifejezések használata.
=val, that each
program has available to it. Users generally place values into the
environment in order to provide information to various programs. Typical
examples are the environment variables HOME and PATH.
awk reads an input record, it splits the record into pieces
separated by whitespace (or by a separator regexp which you can
change by setting the built-in variable FS). Such pieces are
called fields. If the pieces are of fixed length, you can use the built-in
variable FIELDWIDTHS to describe their lengths.
See section Hogyan történik a mezőelválasztás,
and also see
See section Meghatározott szélességű adatok beolvasása.
printf statement. Also, data conversions from numbers to strings
are controlled by the format string contained in the built-in variable
CONVFMT. See section Formátumleíró betűk.
awk has a number of built-in
functions, and also allows you to define your own.
See section Beépített függvények,
and section Felhasználó által definiált függvények.
gawk
awk.
gawk and its source
code may be distributed. (see section GNU GENERAL PUBLIC LICENSE)
0-9 and
A-F, with `A'
representing 10, `B' representing 11, and so on up to `F' for 15.
Hexadecimal numbers are written in C using a leading `0x',
to indicate their base. Thus, 0x12 is 18 (one times 16 plus 2).
awk. Usually, an awk input
record consists of one line of text.
See section Hogyan történik a feldarabolás rekordokra.
awk language, a keyword is a word that has special
meaning. Keywords are reserved and may not be used as variable names.
gawk's keywords are:
BEGIN,
END,
if,
else,
while,
do...while,
for,
for...in,
break,
continue,
delete,
next,
nextfile,
function,
func,
and exit.
awk. Often called Boolean
expressions, after the mathematician who pioneered this kind of
mathematical logic.
awk, a field designator can also be used as an
lvalue.
awk programs by placing two double-quote characters next to
each other (""). It can appear in input data by having two successive
occurrences of the field separator appear next to each other.
gawk implementation uses double
precision floating point to represent numbers.
Very old awk implementations use single precision floating
point.
0-7.
Octal numbers are written in C using a leading `0',
to indicate their base. Thus, 013 is 11 (one times 8 plus 3).
awk which input records are interesting to which
rules.
A pattern is an arbitrary conditional expression against which input is
tested. If the condition is satisfied, the pattern is said to match
the input record. A typical pattern might compare the input record against
a regular expression. See section Minta elemek.
awk users is
IEEE Standard for Information Technology, Standard 1003.2-1992,
Portable Operating System Interface (POSIX) Part 2: Shell and Utilities.
Informally, this standard is often referred to as simply "P1003.2."
awk program. Special care must be
taken when naming such variables and functions.
See section Naming Library Function Global Variables.
awk to process, or it can
specify single lines. See section Minta elemek.
print and printf statements
to a file or a system command, using the `>', `>>', and `|'
operators. You can redirect input to the getline statement using
the `<' and `|' operators.
See section A print és a printf kimenetének átirányítása,
and section Explicit beolvasás getline-al.
awk, regexps are
used in patterns and in conditional expressions. Regexps may contain
escape sequences. See section Reguláris kifejezések.
/foo/. This regular expression is chosen
when you write the awk program, and cannot be changed doing
its execution. See section Hogyan használjuk a reguláris kifejezéseket.
awk program that specifies how to process single
input records. A rule consists of a pattern and an action.
awk reads an input record; then, for each rule, if the input record
satisfies the rule's pattern, awk executes the rule's action.
Otherwise, the rule does nothing for that input record.
awk, essentially every expression has a value. These values
are rvalues.
sed
awk logical operators `&&' and `||'.
If the value of the entire expression can be deduced from evaluating just
the left-hand side of these operators, the right-hand side will not
be evaluated
(see section Logikai kifejezések).
awk to store
numeric values. It is the C type float.
gawk, instead of being handed
directly to the underlying operating system. For example, `/dev/stderr'.
See section Speciális file nevek gawk-ban.
awk language, and may contain escape sequences.
See section Escape szekvenciák.
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place --- Suite 330, Boston, MA 02111-1307, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and modification follow.
NO WARRANTY
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
one line to give the program's name and an idea of what it does. Copyright (C) 19yy name of author This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place --- Suite 330, Boston, MA 02111-1307, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice
This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License.
Jump to: ! - # - $ - & - - - / - < - = - > - \ - _ - a - b - c - d - e - f - g - h - i - j - k - l - m - n - o - p - r - s - t - u - v - w - | - ~ - á - é - ö - ú - ü
! operátor
!= operátor
!~ operátor, !~ operátor, !~ operátor, !~ operátor, !~ operátor
# (megjegyzés)
#! (futtatható script-ek)
$ (mező operátor)
&& operátor
--assign opció
--compat opció
--copyleft opció
--copyright opció
--field-separator opció
--file opció
--help opció
--lint opció
--lint-old opció
--posix opció
--source opció
--traditional opció
--usage opció
--version opció
-F opció, -F opció
-f opció, -f opció
-v opció
-W opció
< operátor
<= operátor
== operátor
> operátor
>= operátor
csh-ben, `\' jel használata, sor folytatás csh-ben
\< regexp operátor
\> regexp operátor
\B regexp operátor
\W regexp operátor
\w regexp operátor
\y regexp operátor
gawk
awk
awk
ftp, anonymous ftp
assert, C verzió
awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió, awk nyelv, POSIX verzió
awk nyelv, V.4 verzió, awk nyelv, V.4 verzió, awk nyelv, V.4 verzió, awk nyelv, V.4 verzió
AWKPATH környezeti változó
awksed
BEGIN speciális minta
break kifejezés
break, cikluson kívül
comp.lang.awk
continue kifejezés
continue, cikluson kívül
csh, sor folytatás, `\' jel használata, csh, sor folytatás, `\' jel használata
custom.h konfigurációs file
cut segédprogram
delete kifejezés
awk programok, dokumentálás, awk programok
egrep, egrep
egrep segédprogram
END speciális minta
sub stb.
exit kifejezés
awk program
getline parancs
FILENAME beállítása getline-al
for (x in ...)
for kifejezés
ftp, anonymous, ftp, anonymous
awk programok
gawk futtatása
gawk kódolási stílus
getgrent, C verzió
getline, visszatérési érték
getline, FILENAME beállítása
getopt, C verzió
getpwent, C verzió
grcat program
gsub, harmadik argumentuma
awk
gawk
awk
BEGIN és az END -en belül
id segédprogram
if-else kifejezés
IGNORECASE és tömb indexek
in operátor
getline parancs
awk és más programok
OFMT
OFS
ORS
gawk
gawk-ban
AWKPATH
POSIXLY_CORRECT
gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk, különbségek, gawk és awk
gawk
mawk
gawk
$
FS
NF
awk-ot
BEGIN
END
awk-ban
next file kifejezés
next kifejezés
next, felhasználó által definiált függvényen belül
nextfile függvény
nextfile kifejezés
awk
FS beállítása
gawk
awk, POSIX awk, POSIX awk, POSIX awk, POSIX awk, POSIX awk, POSIX awk, POSIX awk, POSIX awk, POSIX awk, POSIX awk, POSIX awk, POSIX awk, POSIX awk, POSIX awk, POSIX awk, POSIX awk, POSIX awk, POSIX awk
POSIXLY_CORRECT környezeti változó
print kifejezés
printf kifejezés formátuma
printf, formátum módosító
printf, formátumleíró betűk
awk
pwcat program
RT
RS
NR, FNR
return kifejezés
awk
awk vs. új awk
sed segédprogram, sed segédprogram, sed segédprogram
csh-ben, `\' jel használata, sor folytatás csh-ben, `\' jel használata
split segédprogram
sub, harmadik argumentuma
tee segédprogram
IGNORECASE
in operátor
for kifejezés
awk
uniq segédprogram
wc segédprogram
while kifejezés
|| operátor
~ operátor, ~ operátor, ~ operátor, ~ operátor, ~ operátor
awk
awk vs. régi awk
This document was generated on 9 May 2000 using texi2html 1.56k.