AWK Tutorial: 25 praktických príkladov príkazu AWK v Linuxe


Zaujíma vás, ako používať príkaz AWK v systéme Linux? Tu je 25 príkladov príkazov AWK s náležitým vysvetlením, ktoré vám pomôžu zvládnuť základy AWK.

Príkaz AWK sa datuje do raných čias Unixu. Je súčasťou štandardu POSIX a mal by byť dostupný na akomkoľvek systéme podobnom Unixu. A za.

Aj keď je AWK niekedy zdiskreditovaný kvôli svojmu veku alebo nedostatku funkcií v porovnaní s viacúčelovým jazykom, akým je Perl, zostáva nástrojom, ktorý rád používam pri svojej každodennej práci. Niekedy na písanie relatívne zložitých programov, ale aj vďaka výkonným jednotným modulom môžete písať na vyriešenie problémov s vašimi dátovými súbormi.

Tak presne toto je cieľom tohto článku. Ukážeme vám, ako môžete využiť výkon AWK za menej ako 80 znakov na vykonávanie užitočných úloh. Tento článok nie je úplným tutoriálom AWK, ale aj tak som na začiatok zahrnul niekoľko základných príkazov, takže aj keď nemáte žiadne predchádzajúce skúsenosti, môžete získať základné koncepty AWK.

Moje vzorové súbory pre tento tutoriál AWK

Všetky jednolinky opísané v tomto článku budú testované na rovnakom dátovom súbore:

cat file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Kópiu tohto súboru môžete získať online na GitHub.

Know Preddefinované a automatické premenné v AWK

AWK podporuje niekoľko preddefinovaných a automatických premenných, ktoré vám pomôžu písať vaše programy. Medzi nimi sa často stretnete:

RSOddeľovač záznamov. AWK spracováva vaše údaje jeden záznam po druhom. Separátor záznamov je oddeľovač používaný na rozdelenie vstupného dátového toku do záznamov. Štandardne je to znak nového riadku. Ak ho teda nezmeníte, záznam je jeden riadok vstupného súboru.

NRAktuálne číslo vstupného záznamu. Ak pre svoje záznamy používate štandardný oddeľovač nového riadku, zhoduje sa s aktuálnym číslom vstupného riadku.

FS/OFSZnaky používané ako oddeľovač polí. Keď AWK prečíta záznam, rozdelí ho do rôznych polí na základe hodnotu FS. Keď AWK vytlačí záznam na výstupe, polia sa znova spoja, ale tentoraz s použitím oddeľovača OFS namiesto oddeľovača FS. FS a OFS sú zvyčajne rovnaké, ale nie je to povinné. „biele miesto“ je predvolená hodnota pre obe z nich.

NF – počet polí v aktuálnom zázname. Ak pre svoje polia používate štandardný oddeľovač „bielych miest“, bude sa zhodovať s počtom slov v aktuálnom zázname.

K dispozícii sú aj ďalšie viac-menej štandardné premenné AWK, takže sa oplatí pozrieť si podrobnejšie informácie v príručke na implementáciu konkrétneho AWK. Táto podmnožina však už stačí na to, aby ste mohli začať písať zaujímavé one-linery.

A. Základné použitie príkazu AWK

1. Vytlačte všetky riadky

Tento príklad je väčšinou zbytočný, ale napriek tomu bude dobrým úvodom do syntaxe AWK:

awk '1 { print }' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Programy AWK sa skladajú z jedného alebo viacerých príkazov vzor { akcia }.

Ak sa pre daný záznam („riadok“) vstupného súboru vyhodnotí vzor ako -nulová hodnota (ekvivalent „true “ v AWK), vykonajú sa príkazy v príslušnom bloku akcií. Vo vyššie uvedenom príklade, keďže 1 je nenulová konštanta, akčný blok { print } sa vykoná pre každý vstupný záznam.

Ďalším trikom je { print } je predvolený akčný blok, ktorý použije AWK, ak ho výslovne nešpecifikujete. Vyššie uvedený príkaz teda možno skrátiť takto:

awk 1 file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Nasledujúci AWK program spotrebováva svoj vstup, ale na výstupe nič neprodukuje:

súbor awk 0

2. Odstráňte hlavičku súboru

awk 'NR>1' file
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Pamätajte, že toto je ekvivalent explicitného písania:

awk 'NR>1 { print }' file
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Tento jednoriadkový zapíše záznamy o vstupnom súbore okrem prvého, pretože v tomto prípade je podmienka 1>1, čo zjavne nie je pravda.

Keďže tento program používa predvolené hodnoty pre RS, v praxi zahodí prvý riadok vstupného súboru.

3. Vytlačte riadky v rozsahu

Toto je len zovšeobecnenie predchádzajúceho príkladu a nezaslúži si veľa vysvetlení, okrem toho, že && je logický operátor and:

awk 'NR>1 && NR < 4' file
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team

4. Odstránenie iba prázdnych čiar

awk 'NF' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support
17,05 apr 2019,abhishek,guest

AWK rozdelí každý záznam do polí na základe oddeľovača polí špecifikovaného v premennej FS. Predvolený oddeľovač polí je jeden-alebo-niekoľko-prázdnych-znakov (tiež známe ako medzery alebo tabulátory). S týmito nastaveniami bude každý záznam obsahujúci aspoň jeden znak bez medzery obsahovať aspoň jedno pole.

Inými slovami, jediný prípad, keď NF je 0 (“false ”), je, keď záznam obsahuje iba medzery. Takže tento jednoriadkový vytlačí iba záznamy obsahujúce aspoň jeden znak bez medzery.

5. Odstránenie všetkých prázdnych riadkov

awk '1' RS='' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support

17,05 apr 2019,abhishek,guest

Tento jednoriadkový riadok je založený na nejasnom pravidle POSIX, ktoré určuje, či je RS nastavený na prázdny reťazec, „potom sú záznamy oddelené sekvenciami pozostávajúcimi z <newline> plus jeden alebo viac prázdnych riadkov. ”

V terminológii POSIX stojí za zmienku, že prázdny riadok je úplne prázdny riadok. Riadky, ktoré obsahujú iba medzery, sa nepočítajú ako „prázdne“.

6. Ťažba polí

Toto je pravdepodobne jeden z najbežnejších prípadov použitia pre AWK: extrahovanie niektorých stĺpcov dátového súboru.

awk '{ print $1, $3}' FS=, OFS=, file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
        ,
,
,
17,abhishek

Tu som explicitne nastavil oddeľovače vstupných aj výstupných polí na kómu. Keď AWK rozdelí záznam na polia, uloží obsah prvého poľa do $1, obsah druhého poľa do $2 atď. Nepoužívam to tu, ale stojí za zmienku 0 $je celý záznam.

V tejto jednoliatke ste si mohli všimnúť, že používam akčný blok bez vzoru. V takom prípade sa pre vzor predpokladá 1 („pravda“), takže akčný blok sa vykoná pre každý záznam.

V závislosti od vašich potrieb nemusí produkovať to, čo by sme chceli pre prázdne riadky alebo riadky obsahujúce iba medzery. V takom prípade by druhá verzia mohla byť o niečo lepšia:

awk 'NF { print $1, $3 }' FS=, OFS=, file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
        ,
17,abhishek

V oboch prípadoch som do príkazového riadku odovzdal vlastné hodnoty pre FS a OFS. Ďalšou možnosťou by bolo použiť špeciálny blok BEGIN vo vnútri programu AWK na inicializáciu týchto premenných pred načítaním prvého záznamu. Takže v závislosti od vášho vkusu môžete radšej napísať toto:

awk 'BEGIN { FS=OFS="," } NF { print $1, $3 }' file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
        ,
17,abhishek

Tu stojí za zmienku, že môžete použiť aj bloky END na vykonanie niektorých úloh po prečítaní posledného záznamu. Akoby sme to videli práve teraz. Ako už bolo povedané, pripúšťam, že to nie je ani zďaleka dokonalé, pretože čiary obsahujúce iba biele znaky nie sú spracované elegantne. Čoskoro uvidíme možné riešenie, ale predtým si poďme spočítať...

7. Vykonávanie výpočtov po stĺpcoch

AWK podporuje štandardné aritmetické operátory. A automaticky prevedie hodnoty medzi textom a číslami v závislosti od kontextu. Môžete tiež použiť svoje vlastné premenné na ukladanie stredných hodnôt. Všetko, čo vám umožňuje písať kompaktné programy na vykonávanie výpočtov v dátových stĺpcoch:

awk '{ SUM=SUM+$1 } END { print SUM }' FS=, OFS=, file
263

Alebo ekvivalentne pomocou skrátenej syntaxe +=:

awk '{ SUM+=$1 } END { print SUM }' FS=, OFS=, file
263

Upozorňujeme, že premenné AWK nie je potrebné pred použitím deklarovať. Predpokladá sa, že nedefinovaná premenná obsahuje prázdny reťazec. Čo sa podľa pravidiel prevodu typu AWK rovná číslu 0. Kvôli tejto funkcii som sa neobťažoval explicitne riešiť prípad, keď $1 obsahuje text (v nadpise), medzery alebo jednoducho nič. Vo všetkých týchto prípadoch sa bude počítať ako 0 a nebude zasahovať do nášho súčtu. Samozrejme, bolo by to iné, keby som namiesto toho vykonával násobenia. Prečo by ste teda nepoužili sekciu komentárov na navrhnutie riešenia pre tento prípad?

8. Počítanie počtu neprázdnych riadkov

Pravidlo END som už spomenul predtým. Tu je ďalšia možná aplikácia na počítanie počtu neprázdnych riadkov v súbore:

awk '/./ { COUNT+=1 } END { print COUNT }' file
9

Tu som použil premennú COUNT a zvýšil som ju (+=1) pre každý riadok zodpovedajúci regulárnemu výrazu /./. To znamená, že každý riadok obsahuje aspoň jeden znak. Nakoniec sa blok END používa na zobrazenie konečného výsledku po spracovaní celého súboru. Na názve COUNT nie je nič zvláštne. Mohol som použiť Count, count, n, xxxx alebo akékoľvek iné meno, ktoré je v súlade s pravidlami pre pomenovanie premenných AWK

Je však tento výsledok správny? Závisí to od vašej definície „prázdneho“ riadku. Ak považujete iba prázdne riadky (podľa POSIX) za prázdne, potom je to správne. Ale možno by ste radšej považovali za prázdne aj riadky obsahujúce iba medzery?

awk 'NF { COUNT+=1 } END { print COUNT }' file
8

Tentoraz je výsledok iný, pretože neskoršia verzia ignoruje aj riadky obsahujúce iba medzery, zatiaľ čo pôvodná verzia ignoruje iba prázdne riadky. Vidíte ten rozdiel? Nechám vás, aby ste si to uvedomili sami. Neváhajte použiť sekciu komentárov, ak to nie je dostatočne jasné!

Nakoniec, ak vás zaujímajú iba dátové linky a vzhľadom na môj konkrétny vstupný dátový súbor, mohol by som namiesto toho napísať:

awk '+$1 { COUNT+=1 } END { print COUNT }' file
7

Funguje to vďaka pravidlám konverzie typu AWK. Jednočlenné plus vo vzore si vynúti hodnotenie 1 $v číselnom kontexte. V mojom súbore obsahujú dátové záznamy v prvom poli číslo. Nedátové záznamy (nadpis, prázdne riadky, riadky obsahujúce iba medzery) obsahujú text alebo nič. Všetky sú rovné 0 pri prevode na čísla.

Všimnite si, že pri tomto najnovšom riešení by sa zahodil aj záznam pre používateľa, ktorý má nakoniec 0 kreditov.

B. Používanie polí v AWK

Polia sú silnou funkciou AWK. Všetky polia v AWK sú asociatívne polia, takže umožňujú priradiť ľubovoľný reťazec k inej hodnote. Ak poznáte iné programovacie jazyky, možno ich poznáte ako hash, asociatívne tabuľky, slovníky alebo mapy.

9. Jednoduchý príklad poľa AWK

Predstavme si, že chcem poznať celkový kredit pre všetkých používateľov. Môžem uložiť položku pre každého používateľa v asociatívnom poli a vždy, keď sa stretnem so záznamom pre tohto používateľa, zvýšim zodpovedajúcu hodnotu uloženú v poli.

awk '+$1 { CREDITS[$3]+=$1 }
     END { for (NAME in CREDITS) print NAME, CREDITS[NAME] }' FS=, file
abhishek 17
sonia 129
öle 8
sylvain 109

Priznávam, že toto už nie je jednorazovka. Väčšinou kvôli cyklu for, ktorý sa používa na zobrazenie obsahu poľa po spracovaní súboru. Vráťme sa teda ku kratším príkladom:

10. Identifikácia duplicitných liniek pomocou AWK

Polia, rovnako ako ostatné premenné AWK, môžu byť použité ako v akčných blokoch, tak aj vo vzoroch. Využitím toho môžeme napísať jeden riadok na tlač iba duplicitných riadkov:

awk 'a[$0]++' file
52,01    dec   2018,sonia,team

Operátor ++ je post-inkrementálny operátor zdedený z rodiny jazykov C (ktorej AWK je hrdým členom, vďaka Brianovi Kernighanovi, ktorý bol jedným z jeho pôvodných autorov).

Ako už názov napovedá, operátor po inkrementácii inkrementuje (“add 1”) premennú, ale až potom, čo sa jej hodnota použije na vyhodnotenie zahrnujúceho výrazu.

V takom prípade sa vyhodnotí a[$0], aby sa zistilo, či sa záznam vytlačí alebo nie, a po prijatí rozhodnutia sa vo všetkých prípadoch položka poľa zvýši.

Takže pri prvom čítaní záznamu je a[$0] nedefinované, a teda ekvivalentné nule pre AWK. Takže prvý záznam nie je zapísaný na výstupe. Potom sa tento záznam zmení z nuly na jeden.
Pri druhom čítaní rovnakého vstupného záznamu je a[$0] teraz 1. To je „pravda“. Riadok sa vytlačí. Predtým sa však položka poľa aktualizuje z 1 na 2. A tak ďalej.

11. Odstránenie duplicitných riadkov

Ako dôsledok predchádzajúceho jednoduchého riadku možno budeme chcieť odstrániť duplicitné riadky:

awk '!a[$0]++' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support


17,05 apr 2019,abhishek,guest

Jediný rozdiel je v použití logického operátora (!), ktorý obráti pravdivostnú hodnotu výrazu. To, čo bolo nepravdivé, sa stáva pravdou a to, čo bolo pravdivé, sa stáva klamstvom. Logika nemá absolútne žiadny vplyv na prírastok príspevku ++, ktorý funguje presne ako predtým.

C. Mágia oddeľovača polí a záznamov

12. Zmena oddeľovačov polí

awk '$1=$1' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support

17;05 apr 2019;abhishek;guest

Tento program nastaví premenné FS a OFS tak, aby používali čiarku ako oddeľovač vstupných polí a bodkočiarku ako oddeľovač výstupných polí. Keďže AWK nemení výstupný záznam, pokiaľ ste nezmenili pole, trik $1=$1 sa používa na prinútenie AWK, aby prerušil záznam a znova ho zostavil pomocou oddeľovača výstupných polí.

Pamätajte, že tu je predvolený blok akcie { print }. Takže by ste to mohli prepísať jasnejšie ako:

awk '$1=$1 { print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support

17;05 apr 2019;abhishek;guest

Možno ste si všimli, že oba tieto príklady odstraňujú aj prázdne riadky. prečo? Pamätajte na pravidlá konverzie AWK: prázdny reťazec je „false. Všetky ostatné reťazce sú „pravda“. ” Výraz $1=$1 je afekt, ktorý mení $1. Aj toto je však výraz. A vyhodnotí sa na hodnotu $1 – čo je pre prázdny reťazec „false“. Ak naozaj chcete všetky riadky, možno budete musieť namiesto toho napísať niečo takéto:

awk '($1=$1) || 1 { print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support



17;05 apr 2019;abhishek;guest

Pamätáte si operátor &&? Bolo to logické AND. || je logické ALEBO. Zátvorka je tu potrebná kvôli pravidlám priority operátorov. Bez nich by bol vzor namiesto toho chybne interpretovaný ako $1=($1 || 1). Ako cvičenie som vám nechal otestovať, ako by bol potom výsledok iný.

Nakoniec, ak nie ste príliš nadšení z aritmetiky, stavím sa, že uprednostníte toto jednoduchšie riešenie:

awk '{ $1=$1; print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support



17;05 apr 2019;abhishek;guest

13. Odstránenie viacerých medzier

awk '$1=$1' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest

Toto je takmer rovnaký program ako predchádzajúci. Oddeľovače polí som však ponechal na predvolené hodnoty. Takže ako oddeľovač vstupných polí sa používa viacero medzier, ale ako oddeľovač výstupných polí sa používa iba jedna medzera. Má to pekný vedľajší efekt zlúčenia násobku medzier do jedného priestoru.

14. Spájanie liniek pomocou AWK

Už sme použili OFS, oddeľovač výstupných polí. Ako ste možno uhádli, má náprotivok ORS na určenie oddeľovača výstupných záznamov:

awk '{ print $3 }' FS=, ORS=' ' file; echo
USER sylvain sonia sonia sonia sylvain öle    abhishek

Tu som použil medzeru za každým záznamom namiesto znaku nového riadku. Táto jednovrstvová vložka je v niektorých prípadoch postačujúca, ale stále má určité nevýhody.

Je zrejmé, že nevynecháva riadky obsahujúce iba biele znaky (z toho pochádzajú medzery navyše za öle). Možno teda namiesto toho použijem obyčajný regulárny výraz:

awk '/[^[:space:]]/ { print $3 }' FS=, ORS=' ' file; echo
USER sylvain sonia sonia sonia sylvain öle abhishek

Teraz je to lepšie, ale stále existuje možný problém. Bude to jasnejšie, ak zmeníme oddeľovač na niečo viditeľné:

awk '/[^[:space:]]/ { print $3 }' FS=, ORS='+' file; echo
USER+sylvain+sonia+sonia+sonia+sylvain+öle+abhishek+

Na konci riadku je oddeľovač navyše – pretože oddeľovač polí sa píše za každým záznamom. Vrátane toho posledného.

Aby som to napravil, prepíšem program tak, aby zobrazoval vlastný oddeľovač pred záznamom, počnúc druhým výstupným záznamom.

awk '/[^[:space:]]/ { print SEP $3; SEP="+" }' FS=, ORS='' file; echo
USER+sylvain+sonia+sonia+sonia+sylvain+öle+abhishek

Keďže sa o pridávanie oddeľovača starám sám, nastavil som na prázdny reťazec aj štandardný oddeľovač výstupných záznamov AWK. Keď sa však začnete zaoberať oddeľovačmi alebo formátovaním, môže to byť znak, že by ste mali zvážiť použitie funkcie printf namiesto príkazu print. Ako to uvidíme práve teraz.

D. Formátovanie poľa

Vzťah medzi programovacími jazykmi AWK a C som už spomenul. Okrem iného zo štandardnej knižnice jazyka C AWK zdedí výkonnú funkciu printf, ktorá umožňuje veľkú kontrolu nad formátovaním textu odosielaného na výstup.

Funkcia printf berie formát ako prvý argument, ktorý obsahuje ako obyčajný text, ktorý bude vydaný doslovne, tak aj zástupné znaky použité na formátovanie rôznych častí výstupu. Zástupné znaky sú identifikované znakom %. Najbežnejšie sú %s (pre formátovanie reťazca), %d (pre formátovanie celých čísel) a %f (pre formátovanie čísel s pohyblivou rádovou čiarkou ). Keďže to môže byť dosť abstraktné, pozrime sa na príklad:

awk '+$1 { printf("%s ",  $3) }' FS=, file; echo
sylvain sonia sonia sonia sylvain öle abhishek

Môžete si všimnúť, že na rozdiel od príkazu print funkcia printf nepoužíva OFS a ORS hodnoty. Ak teda chcete nejaký oddeľovač, musíte ho výslovne uviesť, ako som to urobil ja pridaním medzery na koniec formátovacieho reťazca. Toto je cena, ktorú treba zaplatiť za úplnú kontrolu nad výstupom.

Aj keď to vôbec nie je špecifikátor formátu, je to vynikajúca príležitosť predstaviť zápis \n, ktorý možno použiť v akomkoľvek reťazci AWK na reprezentáciu znaku nového riadku.

awk '+$1 { printf("%s\n",  $3) }' FS=, file
sylvain
sonia
sonia
sonia
sylvain
öle
abhishek

15. Vytváranie tabuľkových výsledkov

AWK presadzuje formát údajov záznamu/pole založený na oddeľovačoch. Pomocou funkcie printf však môžete vytvoriť aj tabuľkový výstup s pevnou šírkou. Pretože každý špecifikátor formátu v príkaze printf môže akceptovať voliteľný parameter šírky:

awk '+$1 { printf("%10s | %4d\n",  $3, $1) }' FS=, file
   sylvain |   99
     sonia |   52
     sonia |   52
     sonia |   25
   sylvain |   10
       öle |    8
  abhishek |   17

Ako vidíte, zadaním šírky každého poľa ich AWK doplní doľava medzerami. V prípade textu je zvyčajne vhodnejšie umiestniť výplň vpravo, čo sa dá dosiahnuť použitím záporného čísla šírky. V prípade celých čísel môžeme tiež chcieť doplniť polia nulami namiesto medzier. Dá sa to získať použitím explicitnej 0 pred šírkou poľa:

awk '+$1 { printf("%-10s | %04d\n",  $3, $1) }' FS=, file
sylvain    | 0099
sonia      | 0052
sonia      | 0052
sonia      | 0025
sylvain    | 0010
öle        | 0008
abhishek   | 0017

16. Zaobchádzanie s číslami s pohyblivou rádovou čiarkou

Formát %f si nezaslúži veľa vysvetlení...

awk '+$1 { SUM+=$1; NUM+=1 } END { printf("AVG=%f",SUM/NUM); }' FS=, file
AVG=37.571429

... možno až na to, že takmer vždy chcete explicitne nastaviť šírku poľa a presnosť zobrazeného výsledku:

awk '+$1 { SUM+=$1; NUM+=1 } END { printf("AVG=%6.1f",SUM/NUM); }' FS=, file
AVG=  37.6

Tu je šírka poľa 6, čo znamená, že pole bude zaberať priestor 6 znakov (vrátane bodky a prípadne doplnené medzerami vľavo ako zvyčajne). Presnosť .1 znamená, že chceme zobraziť číslo s 1 desatinným číslom za bodkou. Nechám vás hádať, čo by namiesto toho zobrazil %06.1.

E. Používanie reťazcových funkcií v AWK

Okrem funkcie printf obsahuje AWK niekoľko ďalších pekných funkcií na manipuláciu s reťazcami. V tejto oblasti majú moderné implementácie ako Gawk bohatšiu sadu interných funkcií za cenu nižšej prenosnosti. Pokiaľ ide o mňa, zostanem tu s niekoľkými funkciami definovanými v POSIX, ktoré by mali fungovať rovnako kdekoľvek.

17. Prevod textu na veľké písmená

Tento, používam ho často, pretože dobre rieši problémy s internacionalizáciou:

awk '$3 { print toupper($0); }' file
99,01 JUN 2018,SYLVAIN,TEAM:::ADMIN
52,01    DEC   2018,SONIA,TEAM
52,01    DEC   2018,SONIA,TEAM
25,01    JAN   2019,SONIA,TEAM
10,01 JAN 2019,SYLVAIN,TEAM:::ADMIN
8,12    JUN   2018,ÖLE,TEAM:SUPPORT
17,05 APR 2019,ABHISHEK,GUEST

V skutočnosti je to pravdepodobne najlepšie a najprenosnejšie riešenie na prevod textu na veľké písmená z shellu.

18. Výmena časti struny

Pomocou príkazu substr môžete rozdeliť reťazec znakov na danú dĺžku. Tu ho používam na veľké len prvý znak tretieho poľa:

awk '{ $3 = toupper(substr($3,1,1)) substr($3,2) } $3' FS=, OFS=, file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,Sylvain,team:::admin
52,01    dec   2018,Sonia,team
52,01    dec   2018,Sonia,team
25,01    jan   2019,Sonia,team
10,01 jan 2019,Sylvain,team:::admin
8,12    jun   2018,Öle,team:support
17,05 apr 2019,Abhishek,guest

Funkcia substr berie počiatočný reťazec, (založený na 1) index prvého znaku na extrahovanie a počet znakov na extrahovanie. Ak tento posledný argument chýba, substr prevezme všetky zostávajúce znaky reťazca.

Takže substr($3,1,1) sa vyhodnotí ako prvý znak z $3 a substr($3,2) ako zvyšné tie.

19. Rozdelenie polí v podpoliach

Dátový model záznamového poľa AWK je naozaj pekný. Niekedy však chcete rozdeliť samotné polia na niekoľko častí na základe nejakého vnútorného oddeľovača:

awk '+$1 { split($2, DATE, " "); print $1,$3, DATE[2], DATE[3] }' FS=, OFS=, file
99,sylvain,jun,2018
52,sonia,dec,2018
52,sonia,dec,2018
25,sonia,jan,2019
10,sylvain,jan,2019
8,öle,jun,2018
17,abhishek,apr,2019

Trochu prekvapivo to funguje, aj keď sú niektoré z mojich polí oddelené viac ako jedným prázdnym znakom. Väčšinou z historických dôvodov, keď je oddeľovačom jedna medzera, rozdeliť bude brať do úvahy „prvky sú oddelené medzerami. “ A nielen jedným. Špeciálna premenná FS sa riadi rovnakou konvenciou.

Vo všeobecnom prípade sa však jeden reťazec znakov zhoduje s jedným znakom. Takže, ak potrebujete niečo zložitejšie, musíte si uvedomiť, že oddeľovač polí je rozšírený regulárny výraz.

Ako príklad si pozrime, ako by sa spracovalo pole skupiny, ktoré sa javí ako pole s viacerými hodnotami pomocou dvojbodky ako oddeľovača:

awk '+$1 { split($4, GRP, ":"); print $3, GRP[1], GRP[2] }' FS=, file
sylvain team
sonia team
sonia team
sonia team
sylvain team
öle team support
abhishek guest

Zatiaľ čo by som očakával zobrazenie až dvoch skupín na používateľa, pre väčšinu z nich zobrazuje iba jednu. Tento problém je spôsobený viacerými výskytmi oddeľovača. Takže riešenie je:

awk '+$1 { split($4, GRP, /:+/); print $3, GRP[1], GRP[2] }' FS=, file
sylvain team admin
sonia team
sonia team
sonia team
sylvain team admin
öle team support
abhishek guest

Lomky namiesto úvodzoviek označujú doslovný výraz ako regulárny výraz a nie ako obyčajný reťazec a znamienko plus znamená, že tento výraz sa bude zhodovať s jedným alebo viacerými výskytmi predchádzajúceho znaku. Takže v tomto prípade je každý oddeľovač vytvorený (z najdlhšej postupnosti) jednej alebo niekoľkých po sebe nasledujúcich dvojbodiek.

20. Vyhľadávanie a nahrádzanie príkazmi AWK

Keď už hovoríme o regulárnych výrazoch, niekedy chcete vykonať substitúciu ako príkaz sed s///g, ale iba v jednom poli. V tomto prípade potrebujete príkaz gsub:

awk '+$1 { gsub(/ +/, "-", $2); print }' FS=, file
99 01-jun-2018 sylvain team:::admin
52 01-dec-2018 sonia team
52 01-dec-2018 sonia team
25 01-jan-2019 sonia team
10 01-jan-2019 sylvain team:::admin
8 12-jun-2018 öle team:support
17 05-apr-2019 abhishek guest

Funkcia gsub používa na vyhľadávanie regulárny výraz, náhradný reťazec a premennú obsahujúcu text, ktorý sa má upraviť. Ak to neskôr chýba, predpokladá sa 0 $.

F. Práca s externými príkazmi v AWK

Ďalšou skvelou vlastnosťou AWK je, že môžete jednoducho vyvolať externé príkazy na spracovanie vašich údajov. V zásade existujú dva spôsoby, ako to urobiť: použiť inštrukciu system na vyvolanie programu a nechať ho zmiešať jeho výstup vo výstupnom toku AWK. Alebo pomocou potrubia, aby AWK mohol zachytiť výstup externého programu pre jemnejšiu kontrolu výsledku.

Môžu to byť obrovské témy, ale tu je niekoľko jednoduchých príkladov, ktoré vám ukážu silu týchto funkcií.

21. Pridanie dátumu na začiatok súboru

awk 'BEGIN { printf("UPDATED: "); system("date") } /^UPDATED:/ { next } 1' file
UPDATED: Thu Feb 15 00:31:03 CET 2018
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

V tom AWK programe začínam zobrazením práce AKTUALIZOVANÉ. Potom program vyvolá príkaz externého dátumu, ktorý pošle svoj výsledok na výstup hneď za textom vytvoreným AWK v danej fáze.
Zvyšok programu AWK len odstráni aktualizačný príkaz, ktorý sa prípadne nachádza v súbore a vytlačí všetky ostatné riadky (s pravidlom 1).

Všimnite si ďalší príkaz. Používa sa na prerušenie spracovania aktuálneho záznamu. Je to štandardný spôsob ignorovania niektorých záznamov zo vstupného súboru.

22. Úprava poľa externe

V zložitejších prípadoch možno budete musieť zvážiť | getline VARIABLE idióm AWK:

awk '+$1 { CMD | getline $5; close(CMD); print }' CMD="uuid -v4" FS=, OFS=, file
99,01 jun 2018,sylvain,team:::admin,5e5a1bb5-8a47-48ee-b373-16dc8975f725
52,01    dec   2018,sonia,team,2b87e9b9-3e75-4888-bdb8-26a9b34facf3
52,01    dec   2018,sonia,team,a5fc22b5-5388-49be-ac7b-78063cbbe652
25,01    jan   2019,sonia,team,3abb0432-65ef-4916-9702-a6095f3fafe4
10,01 jan 2019,sylvain,team:::admin,592e9e80-b86a-4833-9e58-1fe2428aa2a2
8,12    jun   2018,öle,team:support,3290bdef-fd84-4026-a02c-46338afd4243
17,05 apr 2019,abhishek,guest,e213d756-ac7f-4228-818f-1125cba0810f

Toto spustí príkaz uložený v premennej CMD, prečíta prvý riadok výstupu tohto príkazu a uloží ho do premennej $5.

Venujte zvláštnu pozornosť príkazu close, ktorý je tu kľúčový, pretože chceme, aby AWK vytvoril novú inštanciu externého príkazu zakaždým, keď vykoná príkaz CMD | príkaz getline. Bez príkazu close by sa AWK namiesto toho pokúsil prečítať niekoľko riadkov výstupu z tej istej inštancie príkazu.

23. Vyvolanie dynamicky generovaných príkazov

Príkazy v AWK sú len obyčajné reťazce bez niečoho zvláštneho. Je to operátor potrubia, ktorý spúšťa spustenie externých programov. Takže, ak potrebujete, môžete dynamicky zostaviť ľubovoľné zložité príkazy pomocou funkcií a operátorov na manipuláciu s reťazcami AWK.

awk '+$1 { cmd = sprintf(FMT, $2); cmd | getline $2; close(cmd); print }' FMT='date -I -d "%s"'  FS=, file
99 2018-06-01 sylvain team:::admin
52 2018-12-01 sonia team
52 2018-12-01 sonia team
25 2019-01-01 sonia team
10 2019-01-01 sylvain team:::admin
8 2018-06-12 öle team:support
17 2019-04-05 abhishek guest

S funkciou printf sme sa už stretli. sprintf je veľmi podobný, ale namiesto odoslania na výstup vráti vytvorený reťazec.

24. Spájanie údajov

Aby som vám ukázal účel záverečného vyhlásenia, dovolím vám vyskúšať si posledný príklad:

awk '+$1 { CMD | getline $5; print }' CMD='od -vAn -w4 -t x /dev/urandom' FS=, file
99 01 jun 2018 sylvain team:::admin  1e2a4f52
52 01    dec   2018 sonia team  c23d4b65
52 01    dec   2018 sonia team  347489e5
25 01    jan   2019 sonia team  ba985e55
10 01 jan 2019 sylvain team:::admin  81e9a01c
8 12    jun   2018 öle team:support  4535ba30
17 05 apr 2019 abhishek guest  80a60ec8

Na rozdiel od vyššie uvedeného príkladu s použitím príkazu uuid je tu iba jedna inštancia od spustená počas beží program AWK a pri spracovaní každého záznamu čítame ešte jeden riadok výstupu toho rovnakéhoprocesu.

Záver

Táto rýchla prehliadka AWK určite nemôže nahradiť plnohodnotný kurz alebo návod na tento nástroj. Avšak pre tých z vás, ktorí to nepoznali, dúfam, že vám to poskytlo dostatok nápadov, takže si môžete okamžite pridať AWK do svojho balíka nástrojov.

Na druhej strane, ak ste už boli fanúšikom AWK, možno ste tu našli niekoľko trikov, ktoré môžete použiť, aby ste boli efektívnejší alebo jednoducho zapôsobili na svojich priateľov.

Netvárim sa však, že som vyčerpávajúci. Takže vo všetkých prípadoch neváhajte a podeľte sa o svoje obľúbené AWK jednovrstvové alebo akékoľvek iné AWK tipy pomocou sekcie komentárov nižšie!