YAzyk obrabotki strukturirovannyh tekstov AWK Proizvodstvenno-vnedrencheskij kooperativ "I N T E R F E J S" Dialogovaya Edinaya Mobil'naya Operacionnaya Sistema Demos/P 2.1 YAzyk obrabotki strukturirovannyh tekstov AWK Moskva 1988 Annotaciya YAzyk AWK ispol'zuetsya dlya kombinirovannoj obrabotki simvol'nyh i chislovyh polej v zapisyah. V rezul'tate generi- ruetsya otchet v zaplanirovannoj programmistom forme. Prog- rammy na yazyke AWK mozhno effektivno ispol'zovat' kak fil'try dannyh dlya preobrazovaniya vyvoda odnoj programmy i peredachi rezul'tata fil'tracii na vhod drugoj. V sisteme DEMOS usta- novlen interpretator yazyka AWK, kotoryj poluchil nazvanie awk. 1. Principy raboty interpretatora awk Lyuboj tekst imeet nekotoruyu strukturu, v prostejshem sluchae ee elementami yavlyayutsya stroki i slova teksta. V yazyke AWK tekst rassmatrivaetsya kak spisok zapisej i polej v nih i na etoj osnove vypolnyaetsya nekotoryj opredelennyj programmistom algoritm obrabotki. Dopustim, imeetsya sleduyu- shchij tekst: Sidorov Sidor Sidorovich 1957 g.r. 220 rub sl. Petrov Petr Ivanovich 1962 g.r. 200 rub sl. Ivanov Mihail Konstantinovich 1965 g.r. 180 rub rab. Volkov Leonid Nikolaevich 1950 g.r. 280 rub rab. Semenov Petr Mihajlovich 1958 g.r. 210 rub rab. |tot tekst strukturirovan: zapisi - eto stroki, polya v stro- kah - slova i chisla. V kazhdoj zapisi soderzhitsya po 8 polej, razdelyayushchihsya probelami. Znachashchim (v kachestve razdelitelya) yavlyaetsya tol'ko odin probel mezhdu polyami, ostal'nye ignori- ruyutsya. Rassmotrim neskol'ko prostyh programm na yazyke AWK. Primer 1. AWK-programma vyvodit pervye tri polya iz vos'mi, poryadok polej v vyvode izmenen i pered kazhdoj stro- koj pechataetsya simvol tabulyacii { print( "\t", $2, $3, $1 ); } Operator print vypolnyaetsya dlya vseh vhodnyh zapisej. Posle vypolneniya programmy poluchim: Sidor Sidorovich Sidorov Petr Ivanovich Petrov Mihail Konstantinovich Ivanov Leonid Nikolaevich Volkov Vladimir Mihajlovich Semenov Kak vidno iz programmy, znacheniya polej podstavlyayutsya sleduyu- shchim obrazom: $nomer_polya_v_zapisi Pervomu polyu sootvetstvuet 1. V obshchem sluchae nomerom polya mozhet byt' znachenie vyrazheniya. Znacheniem podstanovki $0 yavlyaetsya vsya zapis'. Primer 2. AWK-programma vyvodit nomera strok posle tabulyacii { print( "\t", NR, $2, $3, $1 ); } Posle vypolneniya programmy poluchim: - 3 - 1 Sidor Sidorovich Sidorov 2 Petr Ivanovich Petrov 3 Mihail Konstantinovich Ivanov 4 Leonid Nikolaevich Volkov 5 Vladimir Mihajlovich Semenov Predopredelennaya peremennaya NR ravna nomeru obrabatyvaemoj zapisi. My vospol'zovalis' ee znacheniem dlya numeracii strok. Primer 3. AWK-programma vyvodit polnoe chislo let na 1988 god kazhdomu licu iz spiska { print("\t", NR, $2, $3, $1, "\t\t", 1988 - $4); } Posle vypolneniya programmy poluchim: 1 Sidor Sidorovich Sidorov 31 2 Petr Ivanovich Petrov 26 3 Mihail Konstantinovich Ivanov 23 4 Leonid Nikolaevich Volkov 38 5 Vladimir Mihajlovich Semenov 30 Primer 4. AWK-programma podschityvaet srednij vozrast i srednyuyu zarabotnuyu platu perechislennyh v spiske lic { age += 1988 - $4; pay += $6; } END { print ("Srednij vozrast:\t", age/NR ); print ("Srednyaya zarplata:\t", pay/NR ); } Posle vypolneniya programmy poluchim: Srednij vozrast: 29.6 Srednyaya zarplata: 218 Kogda neobhodimo obespechit' vyvod rezul'tata po zaversheniyu spiska zapisej, ispol'zuetsya selektor END. Peremennye age i pay opredelyayutsya avtomaticheski kak chisla v moment pervogo ispol'zovaniya. Vyrazheniya vychislyayutsya dlya vseh vhodnyh zapi- sej. Primer 5. AWK-programma podschityvaet srednie vozrast i zarabotnuyu platu rabochih i sluzhashchih v spiske. Dlya vydeleniya strok so svedeniyami o rabochih ispol'zuetsya shablon /rab/, o sluzhashchih - shablon /sl/. SHablony soderzhat obrazcy dlya poiska v polyah zapisi. Dannye vyvodyatsya posle obrabotki vseh - 4 - zapisej. /rab/ { rage += 1988 - $4; rpay += $6; r++; } /sl/ { age += 1988 - $4; pay += $6; c++; } END { print("\t\tSrednij vozrast Srednyaya zarplata\n"); print(" Rabochie:\t", rage/r, "\t", rpay/r ); print("Sluzhashchie:\t", age/c, "\t\t", pay/c ); } Posle vypolneniya programmy poluchim: Srednij vozrast Srednyaya zarplata Rabochie: 30.3333 223.333 Sluzhashchie: 28.5 210 Programma vypolnyaetsya sleduyushchim obrazom. Esli zapis' v kakom-libo iz polej soderzhit obrazec, vypolnyaetsya dejstvie, zapisannoe v figurnyh skobkah ryadom s sootvetstvuyushchim shablo- nom, inache dejstvie ne vypolnyaetsya. Dejstviya, ukazannye posle END, vypolnyayutsya po koncu spiska zapisej. SHablony v primere ispol'zuyutsya kak selektory vhodnyh zapisej: esli v chetvertom primere dejstviya byli vypolneny dlya vseh vhodnyh zapisej, to v etom - tol'ko dlya otobrannyh po obrazcam, uka- zannym v shablonah. Pri etom END ispol'zuetsya kak selektor special'nogo vida: on opredelyaet spisok operatorov AWK- programmy, kotoryj dolzhen vypolnit'sya posle zaversheniya vhod- nogo potoka zapisej. Primer 6. AWK-programma vychislyaet urovni zarabotnoj platy - 5 - BEGIN { Min = 1000; Max = 0; } { if ( $6 <&lt; Min ) { Min = $6; smin = $1 " " $2 " " $3; } if ( $6 >&gt; Max ) { Max = $6; smax = $1 " " $2 " " $3; } } END { print( "\t\tUrovni zarplaty\n" ); print( " Minimal'nyj: ", Min, " (",smin,")" ); print( "Maksimal'nyj: ", Max, " (",smax,")" ); } Posle vypolneniya poluchim: Urovni zarplaty Minimal'nyj: 180 ( Ivanov Mihail Konstantinovich ) Maksimal'nyj: 280 ( Volkov Leonid Nikolaevich ) V etoj programme tri razdela. Pervyj razdel ispol'zuetsya dlya ustanovki nachal'nyh znachenij peremennyh Max i Min eshche do chteniya zapisej iz spiska. Special'nyj selektor BEGIN opre- delyaet spisok operatorov AWK-programmy, kotoryj dolzhen vypolnit'sya do analiza pervoj zapisi iz vhodnogo potoka. Vo vtorom razdele osushchestvlyaetsya sobstvenno obrabotka zapisej. Operatory etogo razdela programmy vypolnyayutsya dlya vseh vhod- nyh zapisej, tak kak selektor ne ukazan. Tretij razdel vypolnyaetsya kogda zavershaetsya spisok zapisej (selektor END). V stroke smin = $1 " " $2 " " $3; peremennoj smin prisvaivayutsya znacheniya pervyh treh polej zapisi, konkatenaciya kotoryh vmeste s probelami, ukazannymi v kavychkah, obrazuet stroku. Takim obrazom znacheniem pere- mennoj smin budet stroka simvolov tipa "Familiya Imya Otchestvo". Sushchestvuet neskol'ko sposobov vyzova interpretatora awk. AWK-programma v fajle: - 6 - awk -f imya_fajla_s_AWK-programmoj vhodnoj_fajl ... Po umolchaniyu razdelitelem zapisej yavlyaetsya simvol novoj stroki, razdelitelem polej - simvol probela i/ili tabulyacii. Simvoly-razdeliteli mozhno yavno opredelit' v programme. Simvol-razdelitel' polej mozhno opredelit' i v komandnoj stroke. Vyzov awk s ukazaniem simvola-razdelitelya polej: awk -Frazdelitel' -f fajl_AWK-programma vhodnoj_fajl ... CHasto AWK-programmy nastol'ko korotki, chto ih celesoobrazno ukazyvat' neposredstvenno v komandnoj stroke, a ne v fajle. Vyzov awk s programmoj v komandnoj stroke: awk -Frazdelitel' 'AWK-programma' vhodnoj_fajl ... awk 'AWK-programma' vhodnoj_fajl ... Interpretator awk, kak i bol'shinstvo drugih programm sis- temy, pozvolyaet vhodnoj_fajl zamenit' na standartnyj vvod. awk -f imya_fajla_s_AWK-programmoj - awk -Frazdelitel' 'AWK-programma' - awk 'AWK-programma' - Esli ne ukazano drugoe, rezul'tat vypolneniya AWK-programmy pechataetsya na ekrane displeya. 2. Peremennye, vyrazheniya i prisvaivaniya v AWK-programmah V yazyke AWK vydelyayut dve gruppy peremennyh: predoprede- lennye i deklarirovannye v programme. Predopredelennye peremennye dostupny dlya podstanovok i izmenenij v programme, ih ishodnye znacheniya ustanavlivayutsya interpretatorom awk v processe zapuska i vypolneniya AWK-programmy. K predoprede- lennym peremennym otnosyatsya: NR nomer tekushchej zapisi; NF chislo polej v tekushchej zapisi; RS razdelitel' zapisej na vvode (simvol); FS razdelitel' polej zapisi na vvode (simvol); ORS razdelitel' zapisej na vyvode AWK-programmy (simvol); OFS razdelitel' polej zapisi na vyvode (simvol); - 7 - OFMT format vyvoda chisel; FILENAME imya vhodnogo fajla (stroka). Po umolchaniyu imeyut mesto sleduyushchie znacheniya predoprede- lennyh peremennyh: RS = "\0"; FS = 'probel(y) i/ili tabulyaciya'; OFS = FS; ORS = RS; OFMT = "%.6g"; Predopredelennym peremennym RS, FS, ORS, OFS, OFMT mozhno prisvaivat' znacheniya v AWK-programme. V yazyke AWK otsutstvuyut deklaraciya i yavnaya inicializa- ciya peremennoj lyubogo tipa. Vsyakoj peremennoj do ee pervogo ispol'zovaniya prisvaivaetsya znachenie "\0" - pustaya stroka. Primenyayutsya sleduyushchie tipy peremennyh: pozicionnaya peremennaya; chislo s plavayushchej tochkoj; stroka simvolov; massiv. Pozicionnaya peremennaya opredelyaet pole zapisi, soderzhimoe kotorogo mozhet byt' otneseno k tipam "stroka" ili "chislo_s_tochkoj" i ispol'zuetsya v vide $nomer_polya_zapisi $(vyrazhenie) Nomer_polya_zapisi mozhet byt' znacheniem vyrazheniya. Znacheniem pozicionnoj peremennoj $0 yavlyaetsya vsya zapis'. Interpretator awk rassmatrivaet peremennuyu kak stroko- vuyu do togo momenta, kogda neobhodimo vypolnit' nekotoruyu operaciyu nad ee znacheniem. V zavisimosti ot konteksta tip znacheniya peremennoj ostaetsya libo strokovym, libo preobrazu- etsya k tipu chislo_s_tochkoj. V dvusmyslennyh sluchayah pere- mennye rassmatrivayutsya kak strokovye. Stroki, kotorye ne mogut byt' interpretirovany kak chisla, v chislovom kontekste budut imet' chislovoe znachenie NOLX. Ustranit' dvusmyslen- nost' mozhno yavnym ukazaniem tipa peremennoj pri prisvaivanii ej znacheniya, naprimer: - 8 - name = 1 ; # prisvoeno znachenie 1.0 name = "1"; # prisvoeno znachenie stroki "1" Pri interpretacii vyrazhenij sushchestvennuyu rol' igraet kon- tekst, naprimer: name = 3 + 2 ; name = 3 + "2" ; name = "3" + "2" ; name = 3 + 2 + "yabloko grusha apel'sin"; name = "yabloko" + "grusha"; V etom primere v pervyh chetyreh sluchayah name ravno 5.0, v pyatom - 0. Massiv ne deklariruetsya, on nachinaet sushchestvovat' v moment pervogo ispol'zovaniya. Indeksy v massive mogut imet' lyuboe nenulevoe znachenie, vklyuchaya nechislovye stroki, eto pozvolyaet ispol'zovat' associativnye massivy. Naprimer, v privedennoj nizhe AWK-programme budet podschitano chislo upomi- nanij ob avtomobilyah razlichnyh marok vo vhodnom tekste: /ZIL/ { Avtomobili["ZIL"]++; } /GAZ/ { Avtomobili["GAZ"]++; } /VAZ/ { Avtomobili["VAZ"]++; } END { print("ZIL : ", Avtomobili["ZIL"]); print("GAZ : ", Avtomobili["GAZ"]); print("VAZ : ", Avtomobili["VAZ"]); } Massivy mozhno ispol'zovat' dlya organizacii takogo algoritma obrabotki dannyh, v kotorom trebuetsya mnogokratnyj prosmotr vhodnogo potoka zapisej. Naprimer, esli ne zabotit'sya o razmerah operativnoj pamyati, to mozhno ves' vhodnoj fajl zapisat' v vide massiva zapisej i po zaversheniyu vhodnogo potoka pristupit' sobstvenno k obrabotke: - 9 - { Massiv_zapisej[NR] = $0 } END { ... programma obrabotki massiva ... } V kachestve imeni (znacheniya) indeksa massiva mozhno ispol'zo- vat' vyrazhenie, naprimer: name["2" * $3] V yazyke AWK ispol'zuyutsya operatory prisvaivaniya = += -= *= /= %= i arifmeticheskie operacii + - * / % ++ -- Oni imeyut tot zhe smysl, chto i v yazyke programmirovaniya Si. Imeyutsya nekotorye osobennosti vypolneniya operacij srav- neniya <&lt; <&lt;= == != >&gt;= >&gt; Esli oba operanda interpretiruyutsya kak chisla, to vypolnyaetsya sravnenie chisel. Esli odin iz operandov yavlyaetsya strokoj simvolov, a drugoj - chislom, to vypolnyaetsya sravnenie strok. Sravnenie strok zaklyuchaetsya v poparnom sravnenii vnutrennih kodov simvolov strok do pervogo neravenstva kodov ili do zaversheniya odnoj iz strok. Rassmotrim primer: { if( $1 <&lt; $2 ) print(NR": $1 =", $1, "; $2 =", $2, "; $1 < $2"); if( $1 == $2 ) print(NR": $1 =", $1, "; $2 =", $2, "; $1 == $2"); if( $1 >&gt; $2 ) print(NR": $1 =", $1, "; $2 =", $2, "; $1 > $2"); } Dopustim, imeetsya sleduyushchij vhodnoj tekst: - 10 - 2.01 2.02 2.01 abc a b aa b aa ab aa ba abc ab ab abc ef abc V rezul'tate vypolneniya programmy poluchim: 1: $1 = 2.01; $2 = 2.02; $1 < $2 2: $1 = 2.01; $2 = abc ; $1 < $2 3: $1 = a ; $2 = b ; $1 < $2 4: $1 = aa ; $2 = b ; $1 < $2 5: $1 = aa ; $2 = ab ; $1 < $2 6: $1 = aa ; $2 = ba ; $1 < $2 7: $1 = abc ; $2 = ab ; $1 > $2 8: $1 = ab ; $2 = abc ; $1 == $2 9: $1 = ef ; $2 = abc ; $1 > $2 V AWK-programmah mozhno ispol'zovat' sleduyushchie logiches- kie operacii: ! (ne) || (ili) && (i) Kak obychno, znacheniem vyrazheniya, soderzhashchego operacii otno- sheniya i/ili logicheskie operacii, yavlyayutsya: istina (ne nol') ili lozh' (nol'). Prioritety operacij v vyrazheniyah analo- gichny ustanovlennym v yazyke Si. Dlya upravleniya poryadkom vypolneniya operacij v vyrazhenii ispol'zuyutsya kruglye skobki. V yazyke AWK imeetsya operaciya, ne predusmotrennaya v Si, - eto operaciya "probel", kotoraya ispol'zuetsya dlya konkatena- cii peremennyh, znacheniya kotoryh interpretiruyutsya kak stro- kovye name = "yabloko " "i grusha"; V etom sluchae znacheniem peremennoj name budet stroka vida "yabloko i grusha" Vmesto simvola probel mozhno ispol'zovat' simvol tabulyacii. Pri ispol'zovanii operacii "probel" uchityvaetsya kontekst, naprimer: - 11 - $1 = "yabloko" $2 = "i" $3 = "grusha" name1 = $3 $2 $1; # 1 name2 = $3" "$2" "$1; # 2 name3 = "Krasnoe " $1; # 3 name4 = 1 2 3 4 5 6 7 8 9; # 4 name5 = 123 789; # 5 name6 = $3$2$1; # 6 name7 = $3 123; # 7 znacheniem peremennoj name1 budet stroka: "grushaiyabloko" Znacheniem peremennoj name2 budet stroka: "grusha i yabloko" Znacheniem peremennoj name3 budet stroka: "Krasnoe yabloko" Znacheniem peremennoj name4 budet stroka: "123456789" Znacheniem peremennoj name7 budet stroka: "grusha123" Znacheniem peremennoj name5 budet stroka: "123789" Iz primera 5 vidno, chto v kachestve znaka operacii "probel" sushchestvenno nalichie lish' odnogo probela mezhdu operandami, ostal'nye ignoriruyutsya. Znacheniem peremennoj name6 budet stroka vida "grushaiyabloko" Odnako sintaksis, ispol'zovannyj v 6 stroke primera, somni- telen i ne stoit polagat'sya na "mudrost'" interpretatora awk. Pozicionnye peremennye mozhno ispol'zovat' v vyrazheniyah lyubogo vida, im mozhno prisvaivat' novye znacheniya. Rassmotrim neskol'ko primerov: - 12 - $3 = $1 " " $2; $3 += $1; $3 = $3 $3 $3; $3 = ""; $0 = $3; V pervom sluchae pozicionnoj peremennoj $3 prisvaivaetsya stroka, poluchennaya v rezul'tate konkatenacii znacheniya pozi- cionnoj peremennoj $1, probela i znacheniya pozicionnoj pere- mennoj $2. Vo vtorom sluchae znachenie peremennoj $3 uvelichi- vaetsya na znachenie peremennoj $1. V tret'em sluchae vypolnya- etsya konkatenaciya sobstvennogo znacheniya peremennoj $3, v chetvertom - peremennoj $3 prisvaivaetsya znacheniie pustoj stroki, v pyatom - znachenie peremennoj $0 (vsya zapis') zame- nyaetsya znacheniem polya 3. 3. Struktura AWK-programmy AWK-programma sostoit iz spiska pravil vida: selektor1 { dejstvie } ... selektorN { dejstvie } Otkryvayushchaya figurnaya skobka dolzhna stoyat' v toj zhe stroke, gde selektor. V lyubom meste programmy mozhno vvesti kommenta- rij, on pechataetsya ot simvola # do konca stroki. Kazhdoe pravilo vypolnyaetsya dlya kazhdoj zapisi iz vhod- nogo potoka. Selektor ispol'zuetsya dlya togo, chtoby vydelit' zapis', nad kotoroj budet vypolneno dejstvie sootvetstvuyu- shchego pravila. Esli zapis' ne vydelena ni odnim iz selekto- rov, ona ignoriruetsya i ne vyvoditsya na standartnyj vyvod. Esli zapis' vydelena selektorom, vypolnyaetsya dejstvie soot- vetsvuyushchego pravila. Esli nekotoruyu zapis' vydelyayut nes- kol'ko selektorov, nad nej vypolnyayutsya dejstviya sootvetstvu- yushchih pravil. V pravile mozhet otsutstvovat' selektor, togda dejstvie etogo pravila budet vypolneno dlya vseh bez isklyucheniya vhod- nyh zapisej. V pravile mozhet otsutstvovat' dejstvie, togda vse vydelennye selektorom zapisi budut napravleny na stan- dartnyj vyvod bez izmenenij. Opredeleny dva pravila special'nogo vida: - 13 - BEGIN { dejstvie } ... spisok drugih pravil ... END { dejstvie } Pravilo s selektorom BEGIN vypolnyaetsya do chteniya pervoj vhodnoj zapisi, s selektorom END - posle chteniya poslednej zapisi. Pravilo s selektorom BEGIN dolzhno byt' pervym v spiske pravil, s selektorom END - poslednim. Vozmozhno takoe ispol'zovanie etih pravil: BEGIN { dejstvie } ... spisok drugih pravil ili spisok drugih pravil ... END { dejstvie } Dejstvie v pravile mozhet soderzhat' spisok operatorov i upravlyayushchih konstrukcij. Operator dolzhen zakanchivat'sya sim- volom ";", ili simvolom novoj stroki, ili zakryvayushchej skob- koj. Peremennuyu mozhno ispol'zovat' v lyubom pravile AWK- programmy, nachinaya s mesta, gde ona opredelena. Rassmotrim primer, v kotorom demonstriruyutsya osobennosti vypolneniya pravil AWK-programmy i ispol'zovaniya peremennyh: - 14 - # Programma demonstriruet rabotu # pravil razlichnogo vida i oblast' # dejstviya peremennyh # Pravilo 1 vypolnyaetsya # dlya vseh zapisej { print("Zapis' nomer:", NR); } # Pravilo 2 vypolnyaetsya tol'ko dlya # zapisej, gde obnaruzhen obrazec aaa /aaa/ { print("Pravilo 2:"); print(" Vhod:", $0); $1 = $1 $2; $2 = "***"; A = $2; print("Rezul'tat:", $0, "A =", A); } # Pravilo 3 vypolnyaetsya tol'ko dlya # zapisej, gde obnaruzhen obrazec ddd /ddd/ { print("Pravilo 3:"); print(" Vhod:", $0); $1 = $1 $3; $2 = "&&&"; A = $2; print("Rezul'tat:", $0, "A =", A); } # Pravilo 4 vypolnyaetsya dlya vseh zapisej { print("Pravilo 4:", $0, "A =", A,"\n"); } Dopustim, na vhod etoj programme peredayutsya sleduyushchie tri zapisi: eee fff ddd eee fff aaa bbb ccc ddd eee togda posle vypolneniya programmy poluchim: - 15 - Zapis' nomer: 1 Pravilo 4: eee fff A = Zapis' nomer: 2 Pravilo 3: Vhod: ddd eee fff Rezul'tat: dddfff &&& fff A = &&& Pravilo 4: dddfff &&& fff A = &&& Zapis' nomer: 3 Pravilo 2: Vhod: aaa bbb ccc ddd eee Rezul'tat: aaabbb *** ccc ddd eee A = *** Pravilo 3: Vhod: aaabbb *** ccc ddd eee Rezul'tat: aaabbbccc &&& ccc ddd eee A = &&& Pravilo 4: aaabbbccc &&& ccc ddd eee A = &&& 4. Selektory Selektor ukazyvaetsya, chtoby opredelit', budet li vypol- nyat'sya dejstvie v pravile. V kachestve selektora mozhet byt' ispol'zovano lyuboe vyrazhenie, shablon i proizvol'naya ih kom- binaciya. Rassmotrim neskol'ko primerov ispol'zovaniya vyrazhe- nij v selektorah: $1 != $2 || $1 >&gt; 128 vybrat' zapis', v kotoroj libo pervye dva polya raz- lichny, libo soderzhimoe pervogo polya bol'she 128; $1 % $2 == 1 vybrat' zapis', v kotoroj ostatok ot deleniya polej raven 1; NF % 2 == 0 || name <&lt; 2.2 vybrat' zapis', libo soderzhashchuyu chetnoe chislo polej, libo esli peremennaya name men'she 2.2; $1 == "Ivanov I.I." vybrat' zapis', v kotoroj pervoe pole otnositsya k Iva- novu I.I.; $1 >&gt;= "M" && $1 != "Moskva" vybrat' zapis', pervoe pole kotoroj nachinaetsya s bukvy M i dalee po alfavitu, no ne yavlyaetsya slovom Moskva. SHablon ispol'zuetsya dlya formirovaniya odnogo ili bol'- shego chisla obrazcov v selektore. Pri skanirovanii vhodnoj zapisi osushchestvlyaetsya poisk cepochki simvolov, tozhdestvennoj - 16 - obrazcu. V prostejshem sluchae selektor s shablonom vyglyadit sleduyushchim obrazom: /obrazec/ V simvolah / ukazan obrazec, kotoryj budet ispol'zovan dlya poiska. Sushchestvenno, chto lyuboj simvol, v tom chisle probel, ukazannyj vnutri pary simvolov /, yavlyaetsya chast'yu obrazca. Esli neobhodimo, chtoby sootvetstvie obrazcu opredelya- los' v konkretnom pole zapisi, ispol'zuyutsya operatory soot- vetstviya (~ i !~) $nomer_polya ~ shablon esli pri prosmotre ukazannoj pozicionnoj permennoj obnaruzhivaetsya cepochka simvolov, tozhdestvennaya obrazcu v shablone (operator ~), vypolnyaetsya dejstvie pravila. $nomer_polya !~ shablon esli pri prosmotre ukazannoj pozicionnoj permennoj ne obnaruzhivaetsya cepochka simvolov, tozhdestvennaya obrazcu v shablone (operator !~), vypolnyaetsya dejstvie pravila. V obshchem sluchae shablon mozhet formirovat' mnozhestvo obrazcov i/ili ukazyvat', v kakom meste zapisi neobhodimo iskat' sootvetstvie vhodnoj cepochki simvolov obrazcu. Pri neobhodimosti ispol'zuyutsya tak nazyvaemye regulyarnye vyrazhe- niya, v etom sluchae shablon vyglyadit sleduyushchim obrazom: /regulyarnoe_vyrazhenie/ V rezul'tate razbora regulyarnogo vyrazheniya interpretatorom awk stroitsya i vypolnyaetsya algoritm poiska odnogo ili bol'- shego chisla obrazcov vo vhodnoj zapisi. Regulyarnye vyrazheniya v shablonah selektorov AWK analo- gichny podobnym v lex, redaktore ed i v komande grep. Regu- lyarnoe vyrazhenie formiruetsya kak kompoziciya cepochek simvolov (i/ili diapozonov simvolov) i operatorov. Operatory v regu- lyarnyh vyrazheniyah ukazyvayutsya v vide simvolov-operatorov. CHtoby otnesti dejstvie simvola-operatora k otdel'nomu frag- mentu regulyarnogo vyrazheniya, ispol'zuyutsya kruglye skobki. CHtoby otmenit' special'noe znachenie simvola-operatora, ego ekraniruyut simvolom \. Dlya zapisi regulyarnyh vyrazhenij upotreblyayutsya sleduyushchie simvoly-operatory: ^ ot nachala; $ na konce; - 17 - . lyuboj simvol; simvol dannyj simvol, esli on ne simvol-operator; \simvol ispol'zovat' simvol-operator kak obychnyj simvol; [stroka] lyuboj iz simvolov dannoj stroki; [bukva1-bukva2] lyubaya bukva iz dannogo leksikograficheski uporyadochennogo diapazona bukv; [cifra1-cifra2] lyubaya cifra iz dannogo diapazona cifr; reg_vyrazhenie* 0 ili bolee vhozhdenij regulyarnogo vyrazheniya; reg_vyrazhenie+ 1 ili bolee vhozhdenij regulyarnogo vyrazheniya; reg_vyrazhenie? 0 ili 1 vhozhdenie regulyarnogo vyrazheniya; reg_vyrazhenie1 reg_vyrazhenie2 posledovatel'noe vhozhdenie reg_vyrazhenie1 i reg_vyrazhenie2; reg_vyrazhenie1|reg_vyrazhenie2 vhozhdenie reg_vyrazhenie1 ili reg_vyrazhenie2; Rassmotrim neskol'ko primerov ispol'zovaniya regulyarnyh vyrazhenij: /^Ivanov/ vydelit' zapis', nachinayushchuyusya cepochkoj simvolov "Iva- nov" Takim obrazom, budut vydeleny sluchai tipa "Iva- novu", "Ivanovoj", ... ; $3 ~ /^Ivanov/ vydelit' zapis', v kotoroj tret'e pole nachinaetsya cepochkoj simvolov "Ivanov"; /([abc][ABC])$/ vydelit' zapis', predposlednim simvolom kotoroj yavlya- etsya odna iz bukv abc i poslednim - odna iz bukv ABC; /[0-9]+/ vydelit' zapis', soderzhashchuyu ne menee odnoj cifry; - 18 - $3 !~ /(Sidor)|(Petr)/ ne vydelyat' zapis', soderzhashchuyu v tret'em pole chto-libo o Sidorah ili Petrah; Nizhe priveden primer AWK-programmy, pechatayushchej imena registracionnyh katalogov i imena vseh pol'zovatelej sis- temy, kotorymi ne ustanovlen parol': BEGIN { FS = ":"; print("Imya\tKatalog"); } $2 !~ /(([0-9])|([a-z])|([A-Z]))+/ { print( $1, "\t", $6); } V pervom pravile (selektor BEGIN) menyaetsya razdelitel' polej zapisi s probela na dvoetochie (takova struktura zapisej v parol'nom fajle /etc/passwd operacionnoj sistemy DEMOS). Vo vtorom pole zapisi parol'nogo fajla soderzhitsya zashifrovannyj parol' - obychno eto kombinaciya cifr i bukv. Esli parol' ne ustanovlen, to vtoroe pole zapisi pusto. |tot fakt ispol'zo- van dlya formirovaniya selektora - vtoroe pole ne dolzhno soderzhat' cifr i bukv. Selektor vydelyaet vtoroe pole zapisi i proveryaet nalichie ne menee odnogo simvola v etom pole. Esli pole pusto, vypolnyaetsya dejstvie, kotoroe zaklyuchaetsya v pechati imeni pol'zovateya (pervoe pole) i imeni registracion- nogo kataloga pol'zovatelya (shestoe pole). Inogda neobhodimo opredelit' diapazon zapisej, dlya kotoryh vypolnyaetsya dejstvie. Naprimer, neobhodimo vyvesti na pechat' zapisi s nomerami ot 10 do 20 vklyuchitel'no. Ili, dopustim, vyvesti na pechat' pole nomer 6 kazhdoj zapisi, nachinaya s toj, v kotoroj vtoroe pole "Petr", do toj, v koto- roj pyatoe pole "Sidor". Dlya opredeleniya diapazona zapisej v selektorah ispol'zuetsya operaciya zapyataya. Do zapyatoj ukazy- vaetsya selektor, vydelyayushchij pervuyu zapis' v diapazone, posle zapyatoj - selektor, vydelyayushchij poslednyuyu zapis' v diapazone. Takim obrazom, my imeem delo s sostavnym selektorom. Dlya vseh zapisej diapazona vypolnyaetsya dejstvie pravila s sos- tavnym selektorom. Rassmotrim primer. Dopustim, imeetsya sleduyushchij fajl: - 19 - sss pole2 pole3 pole4 1 pole1 sss pole3 pole4 2 pole1 pole2 sss pole4 3 pole1 pole2 pole3 sss 4 ttt pole2 pole3 pole4 5 pole1 ttt pole3 pole4 6 pole1 pole2 ttt pole4 7 pole1 pole2 pole3 ttt 8 Dopustim, neobhodimo vyvesti na pechat' diapazon zapisej. Otkryvaet etot diapazon zapis', vtoroe pole kotoroj "sss", i zakryvaet zapis', tret'e pole kotoroj "ttt". Togda programma vyglyadit sleduyushchim obrazom: $2 ~ /sss/, $3 ~ /ttt/ { print( $0 ); } V rezul'tate vypolneniya poluchim: pole1 sss pole3 pole4 2 pole1 pole2 sss pole4 3 pole1 pole2 pole3 sss 4 ttt pole2 pole3 pole4 5 pole1 ttt pole3 pole4 6 pole1 pole2 ttt pole4 7 V odnoj programme mozhno ukazat' neskol'ko pravil s sos- tavnymi selektorami. Pri etom esli vydelennye diapazony perekryvayutsya, to kazhdaya vydelennaya zapis' budet obrabaty- vat'sya neskol'kimi pravilami. Naprimer, dlya togo zhe ishod- nogo fajla ispol'zuetsya sleduyushchaya programma obrabotki: $2 ~ /sss/, $3 ~ /ttt/ { print( $0 ); } $1 ~ /sss/, NR == 5 { print($0, "*"); } NR == 6, NR == 8 { print( $0, "<-" ); } V rezul'tate vypolneniya poluchim: - 20 - sss pole2 pole3 pole4 1 * pole1 sss pole3 pole4 2 pole1 sss pole3 pole4 2 * pole1 pole2 sss pole4 3 pole1 pole2 sss pole4 3 * pole1 pole2 pole3 sss 4 pole1 pole2 pole3 sss 4 * ttt pole2 pole3 pole4 5 ttt pole2 pole3 pole4 5 * pole1 ttt pole3 pole4 6 pole1 ttt pole3 pole4 6 <- pole1 pole2 ttt pole4 7 pole1 pole2 ttt pole4 7 <- pole1 pole2 pole3 ttt 8 <- CHtoby ustranit' effekt peresecheniya diapazonov vydelen- nyh zapisej, tam, gde eto neobhodimo, mozhno ispol'zovat' operator next. |tot operator prekrashchaet obrabotku tekushchej zapisi, upravlenie peredaetsya na nachalo programmy i nachina- etsya razbor sleduyushchej zapisi. Teper' programma budet imet' vid: $2 ~ /sss/, $3 ~ /ttt/ { print( $0 ); next; } $1 ~ /sss/, NR == 5 { print($0, "*"); next; } NR == 6, NR == 8 { print( $0, "<-" ); } V rezul'tate vypolneniya programmy poluchim: sss pole2 pole3 pole4 1 * pole1 sss pole3 pole4 2 pole1 pole2 sss pole4 3 pole1 pole2 pole3 sss 4 ttt pole2 pole3 pole4 5 pole1 ttt pole3 pole4 6 pole1 pole2 ttt pole4 7 pole1 pole2 pole3 ttt 8 * Iz primera vidno, chto v ishodnom spiske ne nashlos' ni odnoj zapisi, kotoraya byla by obrabotana vsemi pravilami i dejst- vie tret'ego pravila programmy ne vypolnyalos' voobshche. - 21 - Esli v rezul'tate vypolneniya pravila s sostavnym selek- torom vydeleno nachalo diapazona zapisej, no ne vydelen ego konec, dejstvie etogo pravila vypolnyaetsya dlya vseh zapisej do konca vvoda. Esli zhe ne obnaruzhena zapis', otkryvayushchaya diapazon zapisej, to dejstvie pravila s sostavnym selektorom ne vypolnyaetsya. 5. Dejstviya Dejstviya v pravilah AWK-programmy opredelyayut algoritm obrabotki vydelennyh selektorom zapisej. Dlya zapisi algo- ritma ispol'zuyutsya prisvaivaniya, vyrazheniya, operatory uprav- leniya, operatory vyvoda, vstroennye funkcii. Vyshe bylo pokazano, chto dejstvie v pravile zapisyvaetsya kak blok (v smysle yazyka programmirovaniya Si). Figurnaya skobka, otkryvayushchaya blok, dolzhna ukazyvat'sya v toj zhe stroke, chto i selektor, zakryvayushchaya - po zaversheniyu bloka. V obshchem sluchae blok mozhet byt' pustym, togda, kak eto bylo skazano vyshe, vse zapisi, vydelennye selektorom, peredayutsya na standartnyj vyvod bez preobrazovanij. K chislu operatorov upravleniya otnosyatsya: exit zavershit' vypolnenie programmy; next perejti k chteniyu sleduyushchej zapisi. Upravlenie pereda- etsya na pervoe pravilo AWK-programmy (esli imeetsya pra- vilo s selektorom BEGIN, to na sleduyushchee za nim); break preryvaet vypolnenie ohvatyvayushchego cikla. Upravlenie peredaetsya na operator, sleduyushchij za ciklom; continue perehod k sleduyushchej iteracii cikla; if(vyrazhenie) { blok_1 } else { blok_2 } esli znachenie vyrazheniya - istina, vypolnyayutsya operatory bloka_1, inache operatory bloka_2. CHast' else mozhno opustit'. Esli blok_1 ili blok_2 soderzhat po odnomu operatoru, figurnye skobki mozhno ne ukazyvat'; while(vyrazhenie) { blok } operatory bloka vypolnyayutsya, poka znachenie vyrazheniya - istina. Esli v bloke tol'ko odin operator, figurnye skobki mozhno ne ukazyvat'; for(vyrazhenie_1; vyrazhenie_2; vyrazhenie_3) { blok } esli znachenie vyrazheniya_2 - istina, vypolnyayutsya - 22 - operatory bloka. Vyrazhenie_1 vychislyaetsya pered pervoj iteraciej cikla, vyrazhenie_3 vychislyaetsya na kazhdoj ite- racii cikla. Esli blok soderzhit odin operator, figurnye skobki mozhno ne ukazyvat'. for( indeks in imya_massiva ) { blok } dlya kazhdogo znacheniya indeksa massiva vypolnyayutsya opera- tory bloka. Znachenie indeksa formiruetsya avtomaticheski na kazhdoj iteracii cikla i ravno znacheniyu, eshche ne ispol'zovannomu v cikle. Esli ispol'zuetsya associativ- nyj massiv, indeks formiruetsya v leksikograficheskom poryadke. Esli v bloke proishodit dobavlenie elementov massiva, rezul'tat vypolneniya cikla nepredskazuem. Esli v bloke izmenyaetsya znachenie indeksa, rezul'tat vypolne- niya cikla nepredskazuem. Vmesto indeksa i/ili imeni massiva mozhno ukazat' vyrazhenie, znachenie kotorogo interpretiruetsya kak indeks i/ili imya massiva. V kachestve uslovnyh vyrazhenij mozhno ispol'zovat' lyubye iz opisannyh vyshe. V vyrazheniyah mozhno primenyat' shablony, operatory ~ i !~. Rassmotrim primer: /aaa/ { if( $3 !~ /fff/ ) print( $0 ); } V zapisi, vydelennoj po selektoru /aaa/, proveryaetsya soot- vetstvie soderzhimogo polya $3 shablonu /fff/. Esli sootvets- vie ne obnaruzheno, pechataesya vsya zapis', inache operator print ne vypolnyaetsya. Teper' rassmotrim primer ispol'zovaniya cikla for po ideksu v associativnom massive. Dopustim, imeetsya spisok zapisej aaa aaa ddd ccc ccc ddd bbb ddd ddd ccc i pust' vypolnyaetsya programma - 23 - /bbb/ { m["bbb"]++; } /ccc/ { m["ccc"]++; } /aaa/ { m["aaa"]++; } /ddd/ { m["ddd"]++; } END { for( i in m ) print("m["i"] =", m[i]); } V kazhdom iz pervyh chetyreh pravil selektorami vydelyayutsya zapisi i podschityvaetsya chislo takih zapisej v associativnom massive s imenem m. Cikl for vypolnyaetsya po zaversheniyu spiska vhodnyh zapisej. V rezul'tate vypolneniya programmy poluchim: m[aaa] = 1 m[bbb] = 1 m[ccc] = 3 m[ddd] = 3 Znacheniem kazhdogo elementa massiva yavlyaetsya chislo vydelennyh selektorami zapisej. V rezul'tate vypolneniya cikla po indeksu v associativnom massive poluchen vyvod znachenij ele- mentov massiva v leksikograficheskom poryadke znachenij indeksa. Nizhe priveden primer programmy, dejstviya kotoroj soder- zhat primery ispol'zovaniya osnovnyh upravlyayushchih konstrukcij. Dopustim, imeetsya sleduyushchij tekst: aaa, aaa, aaa aaa aaa. aaa aaa, aaa, aaa aaa. aaa aaa aaa, aaa aaa. aaa aaa aaa aaa, aaa. aaa; aaa aaa aaa: aaa. aaa aaa; aaa aaa aaa. aaa aaa aaa; aaa; aaa. aaa aaa: aaa aaa; aaa. aaa: aaa aaa; aaa aaa. aaa aaa aaa: aaa: aaa. Trebuetsya poluchit' nekotoruyu statistku o tekste: - 24 - # Programma vychislyaet statisticheskie # harakteristiki teksta. # Razdelitel' zapisej tochka. # Razdelitel' polej probel. # Vyvod rezul'tatov osushchestvlyaetsya # posle zaversheniya vhodnogo teksta. BEGIN { # vydelenie i inicializaciya # peremennyh RS = "."; # razdelitel' zapisej Nw = 0; # chislo slov Nb = 0; # chislo simvolov v slovah Np = 0; # chislo zapyatyh Nd = 0; # chislo dvoetochij Nt = 0; # chislo tochek s zapyatoj } { for( i = 1; i <&lt;= NF; i++ ){ if( $i ~ /,$/ ) { Np++; Nb--; } # Nb--; ne uchityvat' v dline # slova znak prepinaniya if( $i ~ /:$/ ) { Nd++; Nb--; } if( $i ~ /;$/ ) { Nt++; Nb--; } Nb += length( $i ); # dlina slova Nw++; # uvelichit' chislo slov } } END { print("CHislo zapyatyh =", Np); print("CHislo dvoetochij =", Nd); print("CHislo tochek s zapyatoj =", Nt); print("CHislo slov =", Nw); print("CHislo simvolov v slovah =", Nb); print("CHislo predlozhenij =", NR ); print("Srednyaya dlina predl. =", Nw/NR,"(slov)"); print("Srednyaya dlina slova =", Nb/Nw); - 25 - } Nizhe pokazan rezul'tat raboty programmy: CHislo zapyatyh = 6 CHis