@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
awk
című cikke pontosan bemutatta ennek a dokumentumnak és azawk
nyelvnek 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 }' samplekinyomtat 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-listA 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-listEbben 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") }' -| 3Ha 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 }' -| dcaacbaaaEz 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 abcAhogy 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 cEbben 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.
gawk
FS
(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 int
s 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
Anonymousftp
site: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 case
s
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.