Awk-zoekreeks op variabelewaarde. AWK: Voorbeeldprogramma's. Filters en afdrukopdrachten combineren

In dit artikel laten we er enkele zien praktische voorbeelden over het gebruik van AWK op .

Invoering

AWK is vernoemd naar de namen van de auteurs: Alfred Aho, Peter Weinberger en Brian Kernighan. AWK is erg nuttige taal scripts voor tekstverwerking. Deze taal wordt uitgevoerd in een tolk. Hierdoor kan de gebruiker bepaalde invoer verwerken, variabelen definiëren, gebruiken logische operatoren, tekenreeks- en numerieke functies, extraheer gegevens en maak opgemaakte rapporten. De syntaxis van AWK lijkt sterk op de C-taal en is een directe voorloper van Perl. Alle AWK-scripts kunnen worden geconverteerd naar Perl-scripts met behulp van het A2P-hulpprogramma.

Vereisten

De AWK-tolk wel standaard gereedschap, te vinden op elke Linux-distributie. Het gawk-pakket bevat een open-sourceversie van AWK broncode, en afhankelijk van Linux-distributie het kan worden geïnstalleerd vanaf bronbestand of gebruik de gawk- of mawk-pakketten die bij uw specifieke Linux-distributie zijn geleverd.

Installatie

Met superuser-rechten

Ssh root@IP_Adres

Om het opdrachthulpprogramma te installeren AWK-snaren op /Fedora of een andere op gebaseerd op toerental Linux-distributie voert u de volgende opdracht uit:

Yum installeren gaw

In / moet je deze opdracht aanroepen om Gawk te installeren:

Apt-get install gawk

Voorbeelden van AWK-opdrachten

Eenvoudige awk-opdrachten kunnen eenvoudig worden uitgevoerd vanaf de opdrachtregel, en voor complexere taken moeten ze als awk-scripts naar een bestand worden geschreven. Hieronder vindt u enkele bruikbare voorbeelden awk-opdrachten en uitvoerbare scripts.

U kunt de opdracht AWK gebruiken om alleen specifieke kolommen uit een invoerveld af te drukken. Met de onderstaande opdracht kunt u bijvoorbeeld de lijst met IP-adressen achterhalen die met de server zijn verbonden:

Netstat -anp|grep tcp|awk "(print $5)"| knippen -d: -f1 | sorteer | uniq -c | sorteer -n

Dit is erg handig als u onderzoekt of uw server onder spanning staat DoS-aanval of DDoS.

In het volgende voorbeeld gebruiken we AWK om in bepaalde kolommen naar een specifiek patroon te zoeken en actie te ondernemen op basis van het resultaat:

Exim -bpr | grep bevroren | awk("print $3") | xargs exim-Mrm

Bovenstaande opdracht verwijdert alle bevroren berichten e-mail uit de Exim-mailwachtrij.

AWK wordt vaak gebruikt om nuttige en praktische verwerking en tekstmanipulatie. We kunnen AWK bijvoorbeeld gebruiken om duplicaten te verwijderen tekstbestand zonder sortering:

Awk "!x[$0]++" bestand-met-duplicaten > nieuw-bestand-zonder-duplicaten

Met de volgende opdracht worden vijf willekeurige getallen van 0 tot 999 afgedrukt:

Awk "BEGIN ( voor (i = 1; i<= 5; i++) print int(1000 * rand()) }"

Gebruik de volgende opdracht om het aantal regels te tellen in een bestand met de naam "sample_file":

Awk "END (print NR)" voorbeeldbestand

Met de volgende opdracht worden alle regels in het bestand "sample_file" afgedrukt die regels bevatten die beginnen met 'A' of 'a' gevolgd door 're':

Awk "/re/(print)" /opt/sample_file

Voor complexere bewerkingen kunt u de opdracht AWK gebruiken. Als uw website behoorlijk traag werkt, kunt u de volgende opdracht gebruiken om te controleren of er een probleem is met de I/O-schijf (en/of het netwerk, in zeldzame gevallen):

Tac /proc/stat | awk "/^btime/ (up=systime()-$2;print "up " up/86400 "d"); /^cpu / (print "gebruiker" $2/up "%, leuk " $3/up "%, sys " $4/up "%, inactief " $5/up "%, iowait " $6/up "%, steal " $9/up "%\niowait/used " $6 / ($2+$3+$4) ", steal/used " $9 / ($2+$3+$4) )"

IOWAIT betekent hoe lang processen worden geblokkeerd terwijl ze bezig zijn met I/O, voornamelijk schijfopslag of misschien netwerk. STEAL betekent hoe lang processen worden geblokkeerd door CPU-tijdsgeluk op de server. Het bovenstaande wachten op de CPU-tijd van de gebruiker (=USER + NICE + SYSTEM) toont drukke I/O, het bovenstaande staal toont een drukke CPU.

Het volgende script gebruikt een eenvoudige awk-opdracht die het invoerbestand '/etc/passwd' doorzoekt en uitvoer levert met de gebruikersnaam gevolgd door de datum en tijd van de laatste login:

Vi login-check #!/bin/bash voor gebruiker in `awk -F: "(print $1)" /etc/passwd` do echo -n "$user: " Finger $user | grep Laatste als [ $? != 0 ]; dan echo fi klaar

Maak het script uitvoerbaar:

Chmod 755 login-controle

Voer het script uit:

./login-check

U zou de gebruikersaccounts moeten kunnen zien die beschikbaar zijn op de server, en vervolgens de datum en tijd van de laatste login van elke gebruiker.

Conclusie

Er zijn enkele nieuwe talen zoals Perl en Python die gebruikt kunnen worden in plaats van AWK, maar het gebruik van AWK heeft verschillende voordelen zoals:

  • AWK is heel gemakkelijk te herkennen.
  • AWK kan worden gebruikt om bepaalde soorten problemen sneller op te lossen en efficiëntere scripts te maken dan het gebruik van andere tools/talen.
  • AWK is erg handig bij het werken met grote bestanden zoals logbestanden enz., omdat u met behulp van de AWK-opdracht/script een gefilterd en leesbaar rapport kunt maken.

In dit artikel ben ik van plan nuttige voorbeelden te delen die me helpen alledaagse problemen op te lossen en het gebruik van de opdrachtregel aanzienlijk te vereenvoudigen. Voor degenen die nog niet bekend zijn met AWK, raad ik aan dat je deze scripttaal zeker onder de knie hebt, er is niets ingewikkelds aan. Ik ben van plan elk voorbeeld te voorzien van kleine opmerkingen die licht werpen op de nuances van het gebruik van bepaalde operators.
.

We zoeken naar een regel met een parameter bind-adres in het configuratiebestand.

root@debian:~# awk ‘/bind-address/’ /etc/mysql/my.cnf
bindadres = 127.0.0.1
bindadres = 192.168.1.110

Uitleg: AWK heeft de volgende syntaxis en opties.

akelig[-f programmabestand | 'programma'] [-Fdelimiter]
[-v variabele=waarde] [bestand ...]

−F waarde — definieert het scheidingsteken (stelt de waarde van de ingebouwde FS-variabele in);
−f bestand - programmatekst wordt uit een bestand gelezen, in plaats van vanaf de opdrachtregel. Lezen uit meerdere bestanden wordt ondersteund;
−v var=value - het toewijzen van de vereiste waarde aan een variabele;
−− — markeert het einde van de lijst met opties.

Voorbeeld nr. 2

In het bovenstaande voorbeeld wordt er gezocht in een bestand, maar AWK kan ook de uitvoer van een ander commando accepteren. Laten we proberen ons voorbeeld dienovereenkomstig ingewikkelder te maken.

root@debian-wordpress:~# cat /etc/mysql/my.cnf | awk '/bind-adres/'
bindadres = 127.0.0.1
bindadres = 192.168.1.110

Zoals u kunt zien, bleef het uitvoerresultaat precies hetzelfde, hoewel het ontwerp uiteraard ingewikkelder werd. Het moet gezegd worden dat het in dit voorbeeld niet helemaal raadzaam is om de tweede optie te gebruiken, omdat deze ingewikkelder is. Laten we proberen andere situaties te overwegen waarin het gebruik van een dergelijk ontwerp gerechtvaardigd zou zijn.

Voorbeeld nr. 3

Maak een lijst van symbolische koppelingen en paden naar doelbestanden.

root@debian:~# ls -l /bin/ | awk ‘/lrwxrwxrwx/ (print $9, $10, $11)’
bzcmp -> bzdiff
bzegrep -> bzgrep
bzfgrep -> bzgrep
bzless -> bzmeer
minderbestand -> minderpipe
lsmod -> kmod
mt -> /etc/alternatives/mt
nc -> /etc/alternatives/nc
netcat -> /etc/alternatives/netcat
open -> openvt
pidof -> /sbin/killall5
rbash -> bash
rnano -> nano
sh -> streepje
sh.distrib -> streepje

Uitleg: het awk-programma is een sjabloonpaar ( patroon) en acties ( (actie)), evenals definities van door de gebruiker gedefinieerde functies. Het sjabloon en de actie zien er als volgt uit: patroon (actie) De sjabloon of actie kan worden weggelaten. In het eerste geval wordt de actie op elke regel uitgevoerd, in het tweede geval wordt de normale uitvoer naar het scherm uitgevoerd, gelijk aan het commando(afdrukken). Deze trefwoorden kunnen niet worden gecombineerd met andere patronen.

De invoerreeks bestaat doorgaans uit velden, gescheiden door witruimte. (Deze standaardinstelling kan worden gewijzigd met behulp van de ingebouwde variabele FS of opties -F scheidingsteken.) De velden worden aangeduid met $1, $2, …; $0 verwijst naar de hele regel.

Voorbeeld nr. 4

Laten we op basis van de bovenstaande informatie een voorbeeld bekijken waarin het standaardscheidingsteken wordt gewijzigd: bekijk een lijst met alle gebruikers zonder aanvullende informatie.

root@debian:~# awk -F : '( print $1 )' /etc/passwd
wortel
demon
bak
sys
synchroniseren
spellen
man

(Commando-uitvoer verkort)

Uitleg: sinds in het bestand /etc/passwd records worden opgeslagen in het formulier " root:x:0:0:root:/root:/bin/bash", is het heel logisch om een ​​dubbele punt als scheidingsteken te selecteren en het allereerste veld weer te geven ( $1 ) elke regel ( $0 ).

Voorbeeld nr. 5

Allemaal in hetzelfde bestand met gebruikers, u kunt hun aantal tellen.

root@debian:~# awk 'END (print NR)' /etc/passwd
25

Uitleg: Speciale sjablonen BEGINNEN En EINDE kan worden gebruikt om controle te krijgen vóór het lezen van respectievelijk de eerste invoerregel en na het lezen van de laatste invoerregel.

Awk is eigenlijk een stream-editor zoals sed. Je kunt tekst naar dit programma leiden en het kan het regel voor regel manipuleren. Het programma kan ook uit een bestand lezen. Awk is ook een programmeertaal. Dit betekent in feite dat awk alles kan doen wat sed kan, en nog veel meer.

In tegenstelling tot sed kan awk context onthouden, vergelijkingen maken en vele andere dingen die andere programmeertalen kunnen doen. Het is bijvoorbeeld niet beperkt tot één enkele regel. Met de juiste vaardigheid kan het meerdere lijnen VERBINDEN.

Het meest eenvoudige vorm awk ziet er zo uit:

Awk "(some_action hier)"

"Some_action_here" kan een eenvoudige uitdrukking zijn om het resultaat af te drukken, of iets veel complexers. De syntaxis is vergelijkbaar met de programmeertaal "C". Eenvoudig voorbeeld:

Awk "(afdruk $1,$3)"

betekent dat de eerste en derde kolom worden afgedrukt, waarbij kolommen betekenen "dingen gescheiden door witruimte". Witruimte = tab of spatie.

Live voorbeeld:

Echo "1 2 3 4" | awk "(print $1,$3)" 1 3

Deel twee: Wat kan AWK doen?

Het belangrijkste doel van AWK in het leven is om zijn input regel voor regel te manipuleren. awk programma werkt meestal in stijl

Als wat u wilt doen niet in dit model past, past awk mogelijk niet bij uw idee.

De gebruikelijke syntaxis die wordt gebruikt bij awk-programmering kan als volgt worden beschreven:

Awk-voorbeeld (opdracht(en))

Dit betekent dat

“Kijk naar elke invoerregel om te zien of daar een PATROON zit. Als het er is, voer dan uit wat er tussen () staat"

U kunt SAMPLE of COMMAND overslaan

Als u geen patroon opgeeft, wordt de opdracht op ELKE regel toegepast.

Als een commando wordt weggelaten, komt dit neer op het zeggen (druk gewoon de regel af):

(afdrukken)

Specifieke voorbeelden:

Awk "/#/ (print "Er is commentaar op deze regel")" /etc/hosts

zal "Deze regel bevat een commentaar" afdrukken voor elke regel die minstens één "#" ergens op de regel in /etc/hosts bevat

Aanpassing voor de duidelijkheid

Awk "/#/ (print $0 ":\tEr is commentaar op deze regel)" /etc/hosts

Het element "//" in het patroon is één manier om een ​​overeenkomst op te geven. Er zijn ook andere manieren om te bepalen of een string overeenkomt. Bijvoorbeeld,

Awk "$1 == "#" (print "regel begint met hash")" /etc/hosts

komt overeen met rijen waarvan de eerste kolom een ​​enkele "#" is. De reeks tekens "==" betekent een EXACTE MATCH van de GEHELE eerste kolom.

Wijziging voor de duidelijkheid:

Awk "$1 == "#" (print $0 "\tline begint met hash)" /etc/hosts

Als u daarentegen een gedeeltelijke overeenkomst met een specifieke kolom wilt, gebruikt u de operator "~".

Awk "$1 ~ /#/ (print "ERGENS staat er een hash in kolom 1")" /etc/hosts

ONTHOUD DAT DE EERSTE KOLOM NA EEN WITRUIMTE KAN STAAN.

Wijziging voor de duidelijkheid:

Awk "$1 ~ /#/ (print $0 "\tEr is ERGENS een hash in kolom 1)" /etc/hosts

Het invoeren van "#comment" komt overeen

Het invoeren van "#comment" komt OOK overeen

Als u een specifieke overeenkomst wilt van "een tekenreeks die precies begint met # en een spatie", zou u deze gebruiken

Awk "/^# / (doe iets)"

Meerdere wedstrijden

Awk verwerkt ALLE PATRONEN die overeenkomen met de huidige regel. Dus als we het volgende voorbeeld gebruiken,

Awk " /#/ (print "Er is commentaar") $1 == "#" (print "Commentaar in de eerste kolom") /^# / (print "Commentaar helemaal aan het begin") " /etc/hosts

DRIE items worden uitgevoerd voor een regel zoals de volgende:

# Dit is een opmerking

TWEE inzendingen voor

# Dit is een ingesprongen commentaar

en slechts één voor

1.2.3.4 hostnaam # een laatste opmerking

Contexttracking

Niet alle strings zijn gelijk gemaakt, ook al zien ze er hetzelfde uit. Soms wil je iets met een string doen, afhankelijk van de regels die eraan voorafgaan.

Hier snel voorbeeld die de regels "ADDR" afdrukt als u zich niet in de sectie "geheim" bevindt

Awk " /secretstart/ ( geheim=1) /ADDR/ ( if(secret==0) print $0 ) /* $0 is volledige lijn*/ /geheim/ (geheim=0) "

Het volgende zal de inhoud afdrukken die "ADDR" bevat, tenzij de string "secretstart" werd gezien. BESTEL BELANGRIJK. Als je het bijvoorbeeld zo schrijft:

Awk " /ADDR/ ( if(secret==0) print $0 ) /* $0 is de volledige regel */ /secretstart/ ( secret=1) /secretend/ ( secret=0) "

en geef de volgende invoer

ADDR een normaal adres secretstart ADDR een geheim adres ADDR nog een geheim adres een derde geheim ADDR secretend ADDR ook normaal

dan wordt het eerste "geheime" adres afgedrukt. Gezien het feit dat het originele voorbeeld beide geheimen zal verbergen.

Deel drie: Speciale variabelen

We hebben het al gehad over de gebruikelijke awk-syntaxis. Laten we nu naar modieuze dingen gaan kijken.

awk heeft "speciale" overeenkomende tekenreeksen: " BEGINNEN" En " EINDE"

Richtlijn BEGINNENéén keer gebeld voordat er rijen uit de gegevens werden gelezen, nooit meer.

Richtlijn EINDE gebeld nadat alle regels zijn gelezen. Als er meerdere bestanden worden opgegeven, wordt deze pas aangeroepen nadat het meest recente bestand is voltooid.

Normaal gesproken zul je gebruiken BEGINNEN voor verschillende initialisatie, en EINDE om op te sommen of op te ruimen.

BEGIN ( maxerrors=3 ; logfile=/var/log/something ; tmpfile=/tmp/blah) ... ( bla bla bla ) /^header/ ( headercount += 1 ) END ( printf("totaal aantal headers geteld=% d\n", headercount);

In dit voorbeeld wordt het aantal keren geteld dat "header" in het invoerbestand verschijnt en wordt het totaal pas afgedrukt nadat de verwerking van het gehele bestand is voltooid.

AWK heeft ook nog vele andere speciale waarden die u in de sectie () kunt gebruiken. Bijvoorbeeld,

Afdrukken NF

geeft u het totale aantal kolommen (aantal velden) in de huidige rij. BESTANDSNAAM zal de huidige bestandsnaam zijn, wat impliceert dat de bestandsnaam is doorgegeven aan awk in plaats van dat er een pipe wordt gebruikt.

JE KUNT NIET VERANDEREN NF op eigen houtje.

Hetzelfde met variabele NR, waarmee u kunt zien hoeveel rijen u hebt verwerkt. (“Aantal records” - Aantal records)

Er zijn nog meer speciale variabelen, zelfs variabelen die u midden in het programma kunt wijzigen.

Deel vier: Simpele Awk-voorbeelden

Laten we, om te illustreren en te versterken wat er is gezegd, er een paar bekijken specifieke voorbeelden. Voor hen hebben we drie kleine tekstbestanden nodig.

Laten we voor de volgende voorbeelden een field_data.txt-bestand maken met de volgende inhoud:

Rozen zijn rood, viooltjes zijn blauw, suiker is zoet, en jij ook.

Echo -e "Rozen zijn rood,\nVioletten zijn blauw,\nSuiker is zoet,\nEn jij ook." >veldgegevens.txt

Laten we een bestand letters.txt maken met de volgende inhoud

Een bb ccc dddd ggg hh i

IN opdrachtregel het kan als volgt worden gedaan:

Echo -e "a\nbb\nccc\ndddd\nggg\nhh\ni" > letters.txt

Laten we ten slotte een e-mailgegevensbestand maken met de volgende inhoud:

Amelia 555-5553 [e-mailadres beveiligd] F Antonius 555-3412 [e-mailadres beveiligd] Becky 555-7685 [e-mailadres beveiligd] Een wetsvoorstel 555-1675 [e-mailadres beveiligd] Een Broderick 555-0542 [e-mailadres beveiligd] R Camilla 555-2912 [e-mailadres beveiligd] R. Fabius 555-1234 [e-mailadres beveiligd] F Julie 555-6699 [e-mailadres beveiligd] FMartin 555-6480 [e-mailadres beveiligd] Een Samuël 555-3430 [e-mailadres beveiligd] Een Jean-Paul 555-2127 [e-mailadres beveiligd] R

Dit kan als volgt op de opdrachtregel worden gedaan:

Wget https://raw.githubusercontent.com/tdhopper/awk-lessons/master/data/mail-data -O mail-data

Eenvoudig patroon (voorbeeld)

Als we regels nodig hebben die langer zijn dan twee tekens en we de standaardactie willen gebruiken ( afdrukken), dan krijgen we:

Awk "lengte $0 > 2" letters.txt bb ccc dddd ggg hh

$0 is een ingebouwde variabele die een string bevat.

Eenvoudige functie

Als we geen patroon specificeren, komt elke lijn overeen. Een triviale actie zou zijn om elke regel af te drukken:

Awk "(print)" letters.txt a bb ccc dddd ggg hh i

De functie gebruiken lengte als onze actie kunnen we de lengte van elke regel bepalen:

Awk "( afdruklengte)" letters.txt 1 2 3 4 3 2 1

Deze actie geldt onvoorwaardelijk voor de hele rij. We kunnen dit ook expliciet specificeren:

Awk "( afdruklengte $0)" letters.txt 1a 2bb 3ccc 4dddd 3ggg 2hh 1i

Awk heeft speciale besturingselementen voor het uitvoeren van bepaalde code voordat de bestandsinvoer begint en nadat het bestand is voltooid.

Awk "BEGIN (print "HI" ) (print $0) END (print "BYE!" )" letters.txt HI a bb ccc dddd ggg hh i BYE!

Wij kunnen het hebben meer elementen controle tijdens het afdrukken met behulp van afdrukkenf.

Awk "BEGIN ( printf "%-10s %s\n", "Naam", "Nummer" \ printf "%-10s %s\n", "----", "------" ) \ ( printf "%-10s %s\n", $1, $2 )" mailgegevens Naam Nummer ---- ------ Amelia 555-5553 Anthony 555-3412 Becky 555-7685 Bill 555-1675 Broderick 555-0542 Camilla 555-2912 Fabius 555-1234 Julie 555-6699 Martin 555-6480 Samuel 555-3430 Jean-Paul 555-2127

Combineren van samples en functies

Uiteraard kunnen patronen en functies worden gecombineerd, zodat de functie alleen wordt toegepast als de string overeenkomt met het patroon.

We kunnen de lengte van alle regels langer dan 2 tekens afdrukken.

Awk "lengte($0) > 2 (afdruklengte($0) )" letters.txt 3 4 3

Sterker nog, we hoeven Awk niet te beperken tot slechts één patroon! We kunnen een willekeurig aantal patronen hebben, begrensd door puntkomma's of nieuwe regels:

Awk "lengte($0) > 2 ( print "Lang: " lengte($0) ); lengte($0)< 2 { print "Short: " length($0) }" letters.txt Short: 1 Long: 3 Long: 4 Long: 3 Short: 1

Veel velden

Awk is ontworpen voor eenvoudige gegevensverwerking met veel velden achter elkaar. Het veldscheidingsteken kan worden opgegeven met de sleutel -F.

Een voorbeeld van een bestand waarbij het scheidingsteken een spatie is:

Awk "( print )" field_data.txt Rozen zijn rood, viooltjes zijn blauw, suiker is zoet, en jij ook.

Als we een veldscheidingsteken specificeren, kunnen we het tweede veld van elke regel afdrukken:

Awk -F " " "( print $2 )" field_data.txt is zo

We krijgen geen foutmelding als de rij geen overeenkomend veld heeft; we krijgen eenvoudigweg een lege regel te zien:

Awk -F " " "( print $4 )" field_data.txt u.

Omdat het standaardscheidingsteken een spatie is, zou het vorige commando exact hetzelfde resultaat hebben opgeleverd zonder de optie te gebruiken -F. Laten we voor een betekenisvoller voorbeeld een ander bestand maken tarieven.txt met de volgende inhoud:

Pilcrow,Humphrey,3 Pilcrow,Zora,1 Plinius,Oldone,4 Razniecki,Anton,7 Russell,Bertrand,0

Nu geven we aan als scheidingsteken , (komma) en geef de inhoud van de tweede kolom weer:

Awk -F "," "( print $2 )" rates.txt Humphrey Zora Oldone Anton Bertrand

De scheidingstekenexpressie wordt geïnterpreteerd als een reguliere expressie.

Awk -F "((so)?are|is) " "(print "Veld 1: " $1 "\nVeld 2: " $2)" field_data.txt Veld 1: Rozen Veld 2: rood, Veld 1: Viooltjes Veld 2 : blauw, Veld 1: Suiker Veld 2: zoet, Veld 1: En Veld 2: jij.

Reguliere expressies

Patronen kunnen reguliere expressies zijn, en niet alleen maar ingebouwde functies.

Wij kunnen gebruiken reguliere expressies om alle woorden in de Unix-wereld te vinden met 5 klinkers op een rij.

Awk "/(5)/" /usr/share/dict/words cadiueio Chaouia euouae Guauaenok

Variabelen doorgeven aan een programma

Optie -v want Awk stelt ons in staat variabelen in het programma door te geven. We kunnen dit bijvoorbeeld gebruiken voor hardcodeconstanten.

Awk -v pi=3.1415 "BEGIN (print pi)" 3.1415

Wij kunnen ook gebruiken -v om Bash-variabelen door te geven als Awk-variabelen

Awk -v user=$USER "BEGIN (afdrukgebruiker)" mial

If-else-expressies

Als-anders expressies in Awk zien er als volgt uit:

Als (conditie) lichaam-dan

Bijvoorbeeld:

Afdrukkenf "1\n2\n3\n4" | awk \ "( \ if ($1 % 2 == 0) print $1, "is even"; \ else print $1, "is oneven" \ )" 1 is oneven 2 is even 3 is oneven 4 is even

Cycli

Awk bevat verschillende lusexpressies: terwijl, doe even En voor.

Ze hebben de verwachte C-syntaxis.

Awk\"BEGIN(\i=0;\terwijl(i< 5) { print i; i+=1; } \ }" 0 1 2 3 4 awk \ "BEGIN { \ i = 0; \ do { print i; i+=1; } while(i < 0) \ }" 0 awk \ "BEGIN { \ i = 0; \ for(i = 0; i<5; i++) print i \ }" 0 1 2 3 4

voor kan ook een lus door arraysleutels definiëren, welke zal later worden besproken.

Deel vijf: Oproepfuncties

Het volgende onderdeel van AWK zijn al zijn speciale ingebouwde functies.

AWK heeft functies waar de gemiddelde C-programmeur heel blij mee zal zijn. Hier zijn dingen als sin()/cos()/tan(), rand(),index(), sprintf(), tolower(), system()

De functies zijn gegroepeerd en kunnen als volgt worden bekeken:

Wiskundig

+, -, /, *, sin(), cos(), tan(), atan(), sqrt(), rand(), srand()

Ze spreken voor zich, tenminste dat zou ik graag willen denken.

Awk -v pi=3,1415 "BEGIN ( print exp(1), log(exp(1)), sqrt(2), sin(pi), cos(pi), atan2(pi, 2) )" 2,71828 1 1,41421 9,26536 e-05-1 1.00387

Het programma kan genereren willekeurig nummer binnen het bereik (0, 1).

Standaard start Awk vanaf dezelfde start (seed) voor Awk. Als u deze opdracht tweemaal achter elkaar uitvoert, wordt hetzelfde resultaat geretourneerd:

Awk "BEGIN (print rand(); print rand() )" 0,237788 0,291066

Om de start (zaai) in te stellen, kunt u de srand-functie gebruiken:

Awk "BEGIN ( srand(10); print rand(); print rand() )" 0,255219 0,898883 awk "BEGIN ( srand(10); print rand(); print rand() )" 0,255219 0,898883

Functie int retourneert "het gehele getal dat het dichtst bij x ligt tussen x en nul, waarbij de voorloopnul wordt weggegooid."

Awk "BEGIN ( print "int(0.9) = " int(0.9); print "int(-0.9) = " int(-0.9) )" int(0.9) = 0 int(-0.9) = 0

Snaarmanipulatie

  • index() zal u vertellen of, en zo ja waar, een string binnen een substring voorkomt.
  • overeenkomst() vergelijkbaar, maar werkt voor reguliere expressies.
  • sprintf() geeft u manieren om de uitvoer op te maken en onderweg conversies uit te voeren. Dit zou bekend moeten zijn voor iedereen die printf() met C heeft gebruikt. Bijvoorbeeld
newstring=sprintf("één is een getal %d, twee is een string %s\n", één, twee);

"nieuwe tekenreeks afdrukken%D
"" zegt "druk de waarde af die met mij overeenkomt als een decimaal getal"%S

" zegt "druk de waarde af die met mij overeenkomt als een string"

Die. als u twee regels zonder onderbrekingen wilt aaneenschakelen, kunt u EEN manier gebruiken

  • Newstring=sprintf("%s%s", één, twee) lengte()

Functie geeft u gewoon een eenvoudige manier om het aantal tekens op een regel te tellen als u dat nodig heeft. substr(s, m, n) retourneert de subtekenreeks N -tekens vanaf positie M

, geteld vanaf 1.

Awk "( print $1, substr($1, 2, 3) )" field_data.txt Rozen ose Viooltjes iol Suiker uga En nd index(en, t) geeft 'positie in' terug S waarin de lijn voorkomt T

Het patroon voor index is geen reguliere expressie.

Awk "( print $1, index($1, "s") )" field_data.txt Rozen 3 Viooltjes 7 Suiker 0 En 0

overeenkomst(en, r) geeft de positie terug geeft 'positie in' terug waarin de reguliere expressie voorkomt R, of 0 als dit niet gebeurt. Variabelen RSTART En RLENGTE worden ingesteld op de positie en lengte van de overeenkomende string.

overeenkomst- Hoe is dat index behalve dat het patroon een reguliere expressie is.

Awk "( print $1, match($1, "") )" field_data.txt Rozen 3 Viooltjes 7 Suiker 1 En 0 # "Vind drie of meer herhalende letters" awk "( match($1, "(3)"); print $1, "\tpattern start:", RSTART, "\tpattern end:", RLENGTH )" letters.txt een patroonstart: 0 patrooneinde: -1 bb patroonstart: 0 patrooneinde: -1 ccc patroonstart: 1 patroon einde: 3 dddd patroonstart: 1 patrooneinde: 3 ggg patroonstart: 1 patrooneinde: 3 hh patroonstart: 0 patrooneinde: -1 i patroonstart: 0 patrooneinde: -1

splitsing(en, a, fs) splitst een string in een array van elementen a, a, …, a, en retourneert retourneert de subtekenreeks.

De deling gebeurt via reguliere expressie fs of met veldscheidingsteken FS, Als fs niet gegeven. De lege string als veldscheidingsteken splitst de string karakter voor karakter op in een array van elementen.

Awk "BEGIN (print split("It-was_the-best_of-times", output_array, "[-_]"), output_array, output_array )" 6 was de beste

sub(r, t, s) vervangt door waarin de lijn voorkomt eerste keer dat reguliere expressie voorkomt R in de rij geeft 'positie in' terug. Als s niet wordt gegeven, gebruik dan $0

geeft 'positie in' terug is de tekenreeks waarin de vervanging plaatsvindt. In plaats van een nieuwe string terug te sturen terwijl de vervanging is uitgevoerd, wordt het aantal gemaakte vervangingen (0 of 1) geretourneerd.

Awk "BEGIN ( s = "Het was de beste der tijden, het was de slechtste der tijden"; \ print "Aantal vervangen overeenkomsten:", sub("times", "gifs", s); \ print s )" Num. wedstrijden vervangen: 1 Het was de beste gif, het was de slechtste tijd

gsub doet hetzelfde als sub behalve dat alle exemplaren van de reguliere expressie worden vervangen; sub En gsub geef het aantal vervangingen terug.

Awk "BEGIN ( s = "Het was de beste der tijden, het was de slechtste der tijden"; \ print "Aantal vervangen overeenkomsten:", gsub("times", "cats", s); \ print s )" Num. overeenkomsten vervangen: 2 Het was de beste van katten, het was de slechtste van katten sprintf sprintf(fmt, expr, ...) retourneert de string die resulteert uit het formatteren expr ... volgens het printf(3) formaat fmt awk "BEGIN ( x = sprintf("[%8.3f]", 3.141592654); print x )" [ 3.142]

Functies op systeemniveau

systeem() Hiermee kunt u mogelijk ELK uitvoerbaar bestand op het systeem aanroepen. Het doelprogramma bevindt zich mogelijk in uw $PAD, of u kunt het opgeven met een absoluut pad.

Bijvoorbeeld eng

Systeem("rm -rf $HOME");

Systeem("/bin/kill 1")

Als je complexere dingen wilt doen, zul je waarschijnlijk zoiets doen

Sysstring=sprintf("een commando %s %s", arg1, arg2);

systeem(sysstring) dichtbij() is een belangrijk kenmerk dat vaak over het hoofd wordt gezien. Dit komt waarschijnlijk omdat er geen duidelijke oproep is open() systeem(sysstring), daarom denken mensen niet na over de uitdaging

. En voor de meeste doeleinden is dit niet nodig. Maar u MOET DIT DOEN als u met meer dan één uitvoerbestand te maken heeft.

Awk geeft je de mogelijkheid om direct een willekeurig bestand te openen. Bijvoorbeeld

/^bestand/ (druk $3 >> $2 af)

moet de regel "bestandsuitvoer hier-is-een-woord" bevatten, het bestand "uitvoer" openen en "hier-is-een-woord" erin afdrukken.

AWK is "slim" omdat het bijhoudt welke bestanden u opent en deze open HOUDT. Er wordt van uitgegaan dat als u het bestand één keer hebt geopend, u het waarschijnlijk nog een keer zult doen. Helaas betekent dit dat als u VEEL bestanden opent, u mogelijk geen bestandsbeschrijvingen meer heeft. Dus als u weet dat u klaar bent met een bestand, sluit u het. Dus om het bovenstaande voorbeeld te verbeteren, zou je zoiets als de volgende regels moeten gebruiken:

/^bestand/ ( if ($2 != oudbestand) ( close(oudbestand) ); print $3 >> $2 ; oudbestand = $2; )

Deel zes: Arrays

Array-concept

We hebben variabelen al bekeken als namen die een waarde bevatten. Arrays zijn een uitbreiding van variabelen. Arrays zijn variabelen die meer dan één waarde bevatten. Ze kunnen meer dan één waarde bevatten, omdat elke waarde een eigen nummer heeft.

Als je drie waarden nodig hebt, kun je zeggen:

Waarde1=”één”; waarde2=”twee”; waarde3=”drie”;

OF, je kunt gebruiken

Waarden = "één"; waarden = "twee"; waarden = "drie";

Het eerste voorbeeld zijn drie verschillende variabelen met hun eigen namen (die één teken verschillen). Het tweede voorbeeld is een array die uit één variabele bestaat, maar veel waarden bevat, die elk een eigen nummer hebben.

Wanneer u een variabele als array gebruikt, moet u de waarde altijd tussen vierkante haakjes plaatsen. Je kunt elke naam voor een arrayvariabele kiezen, maar vanaf nu kan die naam ALLEEN voor een array worden gebruikt. Je KUNT niets zinvols doen

Waarden = "één"; waarden = "nieuwewaarde";

U KUNT echter waarden opnieuw toewijzen zoals u dat voor normale variabelen zou doen. Die. het volgende IS juist:

Het interessante is dat je, in tegenstelling tot sommige andere talen, niet gedwongen bent alleen cijfers te gebruiken. In de bovenstaande voorbeelden worden , feitelijk geïnterpreteerd als [“1”], [“2”], [“3”]. Dat betekent dat u ook andere tekenreeksen als ID's kunt gebruiken en de array bijna als een database met één kolom kunt behandelen. De officiële naam hiervoor is "associated array".

Getallen["één"]=1; getallen["twee"]=2; nummers afdrukken["één"]; value="twee"; print numbers; value=$1; if(numbers = ""){ print "no such number"; } !}

Wanneer en hoe arrays te gebruiken

Er kunnen verschillende gevallen zijn waarin u ervoor kunt kiezen om arrays te gebruiken. Sommige mensen doen het helemaal zonder arrays als ze met awk werken. Maar dit is niet helemaal de juiste positie: voor arrays zijn er speciale variabelen die bijvoorbeeld de grootte ervan weergeven (het aantal waarden in de array), er zijn constructies die handig zijn voor het opsommen van arrayleden, en sommige functies retourneren een waarde in de vorm van een array. Hoe dan ook, laten we eens kijken naar enkele voorbeelden die van pas kunnen komen.

Informatie opslaan voor later gebruik

Wanneer u awk in een groot shellscript gebruikt, kunt u de informatie in een tijdelijk bestand opslaan. Maar u kunt de benodigde woorden in het geheugen opslaan en ze aan het eind allemaal afdrukken, wat sneller is dan het gebruik van een tijdelijk bestand.

/special/( opgeslagen woorden=$2; lnum+=1; ) END ( count=0; while(opgeslagen woorden != "") ( aantal afdrukken,opgeslagen woorden; aantal+=1; ) )

In plaats van de woorden eenvoudigweg weer te geven, kunt u de sectie EINDE gebruiken om aanvullende bewerkingen uit te voeren die u mogelijk nodig heeft voordat u ze weergeeft.

Als u een unieke index aan waarden wilt toewijzen (om duplicaten te voorkomen), kunt u over het algemeen naar hun waarden verwijzen via hun eigen rijen. Of sla bijvoorbeeld een array op met kolom 3, geïndexeerd op de overeenkomstige waarde in kolom 2.

( threecol[$2]=$3 ) END ( for (v in threecol) ( print v, threecol[v] ) )

Arrays en split()

De andere belangrijke reden om arrays te gebruiken is als je subvelden wilt gebruiken. Stel dat u een rij heeft met verschillende grote divisies en verschillende kleine divisies. Met andere woorden: de velden op het hoogste niveau worden gescheiden door spaties, maar dan krijg je kleinere woorden gescheiden door dubbele punten.

Dit is een regel variabele:veld:type. Hier kunnen meerdere:type:waarden voorkomen

In het bovenstaande voorbeeld heeft het vierde veld, gescheiden door een spatie, subvelden gescheiden door dubbele punten. Stel nu dat u de waarde wilt weten van het tweede subveld in het vierde grote veld. Eén manier om dit te doen is door twee awks aan te roepen die verbonden zijn door een pijp:

Awk "(afdruk $ 4)" | awk -F: "(print $2)"

Een andere manier zou zijn om de waarde van "FS" direct te wijzigen, die het veldscheidingsteken bevat (blijkbaar werkt dit niet bij alle awk-implementaties):

Awk "( nieuwe regel=$4; fs=FS; FS="; $0=nieuwe regel; print $2; FS=fs; )"

Maar je kunt dit ook met arrays doen door de functie split() als volgt te gebruiken:

Awk "( newline=$4; split(newline,subfields, '); print subvelden) "

In dit geval is het gebruik van een array de meest gebruikelijke en misschien wel meest elegante manier om dit te doen.

Awk biedt dus een beperkt aantal datastructuren. Naast scalaire en stringvariabelen heeft de taal een ingebouwde enorme datastructuur. Hoewel het officieel "arrays" wordt genoemd, is deze structuur feitelijk een bijbehorende array, vergelijkbaar met de dict-datastructuur in Python.

Arrays hoeven niet te worden geïnitialiseerd. U kunt gewoon beginnen met het toekennen van waarden. Houd er rekening mee dat sleutels cijfers of tekenreeksen kunnen zijn.

Awk "BEGIN ( \ a = 1.1; \ a = 0; \ a["HOND"] = "KAT"; \ print a, a, a["HOND"] \ )" 1.1 0 KAT

Awk zal geen variabele afdrukken zonder index:

Awk "BEGIN ( \a["DOG"] = "CAT"; \print a\ )" awk: cmd. line:3: fataal: poging om array `a' te gebruiken in een scalaire context

Hoewel we per sleutel kunnen lussen met behulp van voor:

Awk "BEGIN ( \ a = 1.1; \ a = 0; \ a["DOG"] = "CAT"; \ for(k in a) print(a[k]) \ )" CAT 0 1.1

Deel zeven: AWK en Shells (sh/ksh/bash/csh)

Soms is de functionaliteit van AWK misschien niet voldoende. In dit geval kunt u awk in uw shellscript integreren. Hieronder vindt u enkele voorbeelden van hoe dit kan worden gedaan.

Simpele conclusie

Soms wil je awk alleen als formatter gebruiken en de uitvoer rechtstreeks naar de gebruiker dumpen. Het volgende script neemt een gebruikersnaam als argument en gebruikt awk om de gebruikersinformatie uit /etc/passwd te dumpen.

Opmerking: Merk op dat in het script de enkele aanhalingstekens zijn uitgevouwen (niet genest) en dat er tussen de twee uitgevouwen paren enkele aanhalingstekens een variabele $1 (de tweede) is, wat in dit geval het scriptargument is, terwijl $1 deel uitmaakt van de syntaxis $ 1 (betekent het eerste veld in de rij).

#!/bin/sh while [ "$1" != "" ] ; do awk -F: "$1 == ""$1"" ( print $1,$3) " /etc/passwd shift gedaan

Awk-uitvoershellvariabelen toewijzen

Soms willen we awk alleen gebruiken voor een snelle manier om de waarde van een variabele in te stellen. Met behulp van het passwd-thema hebben we een manier om de shell voor de gebruiker te achterhalen en te kijken of deze is opgenomen in de lijst met officiële shells.

Merk opnieuw op hoe de enkele aanhalingstekens worden gesloten in de awk-expressie. Na het gesloten (tweede) aanhalingsteken is $1 de variabele die de waarde van het eerste argument aan het script doorgeeft, en maakt geen deel uit van de awk-syntaxis.

#!/bin/sh user="$1" if [ "$user" == "" ] ; echo dan ERROR: heb een gebruikersnaam nodig; Uitgang ; fi usershell=`awk -F: "$1 == ""$1"" ( print $7) " /etc/passwd` grep -l $usershell /etc/shells if [ $? -ne 0] ; echo dan ERROR: shell $usershell voor gebruiker $user niet in /etc/shells fi

Andere alternatieven:

# Zie "man regex" usershell=`awk -F: "/^"$1":/ ( print $7) " /etc/passwd` echo $usershell; # Alleen moderne awk accepteert -v. Mogelijk moet u "nawk" of "gawk" gebruiken usershell2=`awk -F: -v user=$1 "$1 == user ( print $7) " /etc/passwd` echo $usershell2;

Het uitleggen van aanvullende methoden hierboven wordt als huiswerk voor de lezer overgelaten :)

Gegevens overbrengen naar awk via pipe

Soms wil je awk als gegevensfilter plaatsen, in een groot programma, of als een commando van één regel dat in een shell-prompt wordt ingevoerd. Een voorbeeld van zo'n opdracht in een script (een lijst met logbestanden van de webserver wordt als argumenten aan het script doorgegeven, omdat loggen aanpasbaar is en de logs een andere structuur kunnen hebben; om het in specifieke gevallen te laten werken, kan het nodig zijn om de commando's aan te passen):

#!/bin/sh grep -h "/index.html" $* | awk -F\" "(print $4)" | sorteer -u

  1. Interessant artikel, ik wil je bedanken voor je inspanningen.

    Ik vond het onnauwkeurig. Als u de regel uit het voorbeeld uitvoert

    Awk -F " " "(print $2)" field_data.txt

    het zal hetzelfde opleveren als

    Awk "( print $2 )" field_data.txt

    Het resultaat is een voorbeeld met -F niet goed beschreven.

Een kennismaking met een prachtige taal met een vreemde naam

Inhoud serie:

Ter verdediging van awk

In deze serie artikelen ga ik van de lezer een bekwame programmeur maken. Ik ben het ermee eens dat awk niet de mooiste of meest trendy naam heeft, en de GNU-versie van awk, genaamd gawk, klinkt ronduit raar. Programmeurs die niet bekend zijn met de taal kunnen de naam ervan horen en zich een wirwar van oude en achterhaalde code voorstellen die zelfs de meest deskundige UNIX-specialist gek zou kunnen maken (waardoor hij "kill -9!" zou uitroepen en voortdurend naar de koffie zou rennen).

Ja, awk heeft geen geweldige naam. Maar het is een prachtige taal. Awk is ontworpen voor tekstverwerking en rapportage, maar beschikt over veel goed ontwikkelde functies die serieus programmeren mogelijk maken. In tegenstelling tot sommige andere talen is de syntaxis van awk echter bekend en leent deze het beste van talen als C, python en bash (hoewel awk formeel vóór python en bash werd gemaakt). Awk is een van die talen die, eenmaal geleerd, een belangrijk onderdeel wordt van het strategische arsenaal van een programmeur.

Eerste stap in awk

Laten we aan de slag gaan en experimenteren met awk om te zien hoe het werkt. Voer op de opdrachtregel de volgende opdracht in:

$awk "(print)" /etc/passwd

Het resultaat zou de inhoud van het bestand /etc/passwd moeten tonen. Nu - een uitleg van wat awk deed. Bij het aanroepen van awk hebben we /etc/passwd opgegeven als invoerbestand. Toen we awk uitvoerden, werd het printcommando voor elke regel in /etc/passwd op volgorde verwerkt. Alle uitvoer wordt naar stdout gestuurd en we krijgen hetzelfde resultaat als cat /etc/passwd. Laten we nu het (print)blok uitleggen. in awk beugel worden gebruikt om tekstblokken te groeperen, zoals in C. Ons tekstblok heeft slechts één afdrukcommando. In awk drukt de printopdracht zonder aanvullende parameters de volledige inhoud af huidige lijn.

Hier is nog een voorbeeld van een awk-programma dat hetzelfde doet:

$awk "(print $0)" /etc/passwd

In awk vertegenwoordigt de variabele $0 de gehele huidige regel, dus print en print $0 doen precies hetzelfde. Als je wilt, kun je in awk een programma maken dat gegevens uitvoert die totaal geen verband houden met de invoergegevens. Hier is een voorbeeld:

$awk "(print "" )" /etc/passwd

Wanneer u de string "" doorgeeft aan de printopdracht, wordt er altijd een lege string afgedrukt. Als u dit script test, zult u merken dat awk één lege regel voor elke regel in /etc/passwd uitvoert. Dit gebeurt opnieuw omdat awk voor elke regel in het invoerbestand een script uitvoert. Hier is nog een voorbeeld:

$awk "(print "hoi" )" /etc/passwd

Als u dit script uitvoert, wordt het scherm gevuld met de woorden "yay". :)

Meerdere velden

Awk is zeer geschikt voor het verwerken van tekst die is opgedeeld in meerdere logische velden, en maakt het gemakkelijk om toegang te krijgen tot elk afzonderlijk veld vanuit een awk-script. Het volgende script drukt een lijst af met alle accounts op het systeem:

$ awk -F: "( print $1 )" /etc/passwd

In de awk-aanroep in het bovenstaande voorbeeld specificeert de optie –F eerder als veldscheidingsteken. Bij het verwerken van de opdracht print $1 drukt awk het eerste veld af dat op elke regel van het invoerbestand voorkomt. Hier is nog een voorbeeld:

$ awk -F: "( print $1 $3 )" /etc/passwd

Hier is een fragment uit de schermuitvoer van dit script:

halt7 operator11 root0 shutdown6 sync5 bin1 ....etc.

Zoals je kunt zien, voert awk de eerste en derde velden van het /etc/passwd-bestand uit, dit zijn respectievelijk de gebruikersnaam- en uid-velden. Hoewel het script werkt, is het tegelijkertijd niet perfect: er zijn geen spaties tussen de twee uitvoervelden! Degenen die gewend zijn aan het programmeren in bash of python, hadden misschien verwacht dat het print $1 $3 commando een spatie tussen deze twee velden zou invoegen. Wanneer echter twee regels naast elkaar verschijnen in een awk-programma, voegt awk ze samen zonder een spatie ertussen toe te voegen. Met de volgende opdracht wordt een spatie tussen de velden ingevoegd:

$ awk -F: "( print $1 " " $3 )" /etc/passwd

Wanneer print op deze manier wordt aangeroepen, worden $1, " " en $3 in series samengevoegd, waardoor voor mensen leesbare uitvoer op het scherm ontstaat. Uiteraard kunnen we indien nodig ook veldlabels invoegen:

$ awk -F: "( print "gebruikersnaam: " $1 "\t\tuid: " $3" )" /etc/passwd

Als gevolg hiervan komen we tot de volgende conclusie:

gebruikersnaam: stop uid:7 gebruikersnaam: operator uid:11 gebruikersnaam: root uid:0 gebruikersnaam: shutdown uid:6 gebruikersnaam: sync uid:5 gebruikersnaam: bin uid:1 ....etc.

Externe scripts

Het doorgeven van scripts aan awk als opdrachtregelargumenten kan handig zijn voor kleine oneliners, maar als het om complexe meerregelige programma's gaat, is het beslist beter om het script als een extern bestand samen te stellen. U kunt vervolgens awk naar dit scriptbestand verwijzen met behulp van de optie -f:

$ awk -f mijnscript.awk mijnbestand.in

Door scripts in afzonderlijke tekstbestanden te plaatsen, kunt u ook profiteren van de extra voordelen van awk. Het volgende meerregelige script doet bijvoorbeeld hetzelfde als een van onze vorige oneliners: drukt het eerste veld van elke regel af vanuit /etc/passwd:

BEGIN ( FS= ' ) ( afdrukken $1 )

Het verschil tussen deze twee methoden is de manier waarop we het veldscheidingsteken specificeren. In dit script wordt het veldscheidingsteken intern door het programma zelf gespecificeerd (door de FS-variabele in te stellen), terwijl in ons vorige voorbeeld FS wordt geconfigureerd door de optie -F: op de opdrachtregel door te geven. Het is meestal het beste om het veldscheidingsteken in het script zelf op te geven, eenvoudigweg omdat u dan niet nog een opdrachtregelargument hoeft te onthouden. We zullen verderop in dit artikel meer in detail kijken naar de FS-variabele.

BEGIN- en END-blokken

Normaal gesproken voert awk elk blok in de scripttekst één keer uit voor elke invoerregel. Er zijn echter vaak situaties bij het programmeren waarin u initialisatiecode moet uitvoeren voordat awk begint met het verwerken van tekst uit een invoerbestand. Voor dergelijke gevallen biedt awk de mogelijkheid om een ​​BEGIN-blok te definiëren. In het vorige voorbeeld hebben we het BEGIN-blok gebruikt. Omdat het BEGIN-blok wordt verwerkt voordat awk begint met het verwerken van het invoerbestand, is dit een uitstekende plek om een ​​FS-variabele (field separator) te initialiseren, een header uit te voeren of andere globale variabelen te initialiseren die later in het programma zullen worden gebruikt.

Awk biedt ook nog een speciaal blok, het END-blok. Awk voert dit blok uit nadat alle regels in het invoerbestand zijn verwerkt. Normaal gesproken wordt een END-blok gebruikt om definitieve berekeningen uit te voeren of resultaten uit te voeren die aan het einde van de uitvoerstroom moeten verschijnen.

Reguliere expressies en blokken

Met Awk kunt u reguliere expressies gebruiken om selectief specifieke blokken van een programma uit te voeren, afhankelijk van of de reguliere expressie wel of niet overeenkomt met de huidige regel. Hier is een voorbeeldscript dat alleen die regels afdrukt die de tekenreeks foo bevatten:

/foo/ (afdrukken)

Natuurlijk kunt u complexere reguliere expressies gebruiken. Hier is een script dat alleen tekenreeksen uitvoert die een float bevatten:

/+\.*/ (afdrukken)

Uitdrukkingen en blokken

Er zijn veel andere manieren om een ​​blok van een programma selectief uit te voeren. We kunnen elke Booleaanse expressie vóór een programmablok plaatsen om de uitvoering van dat blok te controleren. Awk zal alleen een programmablok uitvoeren als de vorige Booleaanse expressie waar wordt. Het volgende voorbeeldscript zal het derde veld van alle regels uitvoeren waar het eerste veld fred is. Als het eerste veld van de huidige regel niet fred is, zal awk doorgaan met het verwerken van het bestand en geen printinstructie geven voor de huidige regel:

$1 == "fred" (print $3)

Awk biedt een volledige set vergelijkingsoperatoren, inclusief de gebruikelijke "==", "<", ">", "<=", ">=" en "!=". Bovendien biedt awk de operatoren "~" en "!~", wat "komt overeen" en "komt niet overeen." Ze plaatsen de variabele links van de operator en de reguliere expressie rechts ervan Hier is een voorbeeld waarbij alleen het derde veld van een regel wordt uitgevoerd als het vijfde veld van dezelfde regel de tekenreeks root bevat:

$5 ~ /root/ (druk $3 af)

Voorwaardelijke uitspraken

Awk biedt ook hele mooie C-achtige if-statements. Als je wilt, kun je het vorige script herschrijven met if:

( if ($5 ~ /root/) (print $3 ) )

Beide scripts werken identiek. In het eerste voorbeeld bevindt de Booleaanse expressie zich buiten het blok, terwijl in het tweede voorbeeld het blok voor elke invoerregel wordt uitgevoerd en we de opdracht print selectief uitvoeren met behulp van een if-instructie. Beide methoden werken, en u kunt de ene kiezen dat het beste combineert met andere delen van het script.

Hier is een complexer voorbeeld van een if-instructie in awk. Zoals je kunt zien, zelfs met complexe geneste conditionele regels, als instructies er identiek uitzien als hun C-tegenhangers:

( if ($1 == "foo") ( if ($2 == "foo") (print "uno" ) else (print "één" ) ) else if ($1 == "bar") (print "twee" ) anders (druk "drie" af) )

Met behulp van if-instructies kunnen we deze code transformeren:

! /matchme/ (afdrukken $1 $3 $4 )( if ($0 !~ /matchme/) (print $1 $3 $4 ) )

Beide scripts printen alleen de regels die Niet bevatten de tekenreeks matchme . En ook in dit geval kun je een methode kiezen die beter werkt in een bepaald programma. Ze doen allebei hetzelfde.

Awk geeft je ook de mogelijkheid om de Booleaanse operatoren "||" te gebruiken (“logische OR”) en “&&” (“logische AND”), waarmee u complexere Booleaanse expressies kunt maken:

($1 == "foo") && ($2 == "bar") ( afdrukken )

In dit voorbeeld worden alleen regels weergegeven waarvan het eerste veld foo is En het tweede veld is bar.

Numerieke variabelen!

Tot nu toe hebben we stringvariabelen, volledige strings of specifieke velden afgedrukt. Awk geeft ons echter ook de mogelijkheid om vergelijkingen uit te voeren op zowel gehele getallen als drijvende-kommagetallen. Met behulp van wiskundige uitdrukkingen is het heel eenvoudig om een ​​script te schrijven dat het aantal lege regels in een bestand telt. Hier is zo'n script:

BEGIN ( x=0 ) /^$/ ( x=x+1 ) END ( print "Gevonden " x " lege regels. :)" )

In het BEGIN-blok initialiseren we onze gehele variabele x naar nul. Elke keer dat awk een lege regel tegenkomt, voert het de instructie x=x+1 uit, waarbij x met 1 wordt verhoogd. Zodra alle regels zijn verwerkt, wordt het END-blok uitgevoerd en drukt awk een eindtotaal af dat het aantal aangeeft. van lege regels gevonden.

Tekenreeksvariabelen

Een van de leuke dingen van awk-variabelen is dat ze 'gewoon en in kleine letters' zijn. Ik noem awk-variabelen "string" omdat alle awk-variabelen intern worden opgeslagen als strings. Tegelijkertijd zijn awk-variabelen "eenvoudig" omdat je berekeningen met een variabele kunt uitvoeren, en als deze een geldige getallenreeks bevat, zorgt awk er automatisch voor dat de tekenreeks naar een getal wordt geconverteerd. Om te zien wat ik bedoel, bekijk dit voorbeeld:

x="1.01" # We hebben ervoor gezorgd dat x de *string* "1.01" bevat x=x+1 # We hebben zojuist 1 toegevoegd aan de *string* print x # Dit is trouwens een commentaar :)

Awk zal uitvoeren:

2.01

Nieuwsgierig! Hoewel we de stringwaarde 1.01 aan x hebben toegekend, konden we er toch één aan toevoegen. We konden dit niet doen in bash of python. Allereerst ondersteunt bash geen drijvende-kommaberekeningen. En hoewel bash "string" -variabelen heeft, zijn ze niet "eenvoudig"; Om enige wiskunde te kunnen uitvoeren, vereist bash dat we onze berekeningen in lelijke $()-constructies verpakken. Als we Python zouden gebruiken, zouden we onze string 1.01 expliciet naar een drijvende-kommawaarde moeten converteren voordat we er berekeningen mee zouden uitvoeren. Hoewel dit niet moeilijk is, is het toch een extra stap. In het geval van awk gebeurt dit allemaal automatisch, en het maakt onze code mooi en schoon. Als we het eerste veld van elke invoertekenreeks vierkant zouden moeten maken en er één aan moeten toevoegen, zouden we een script als dit gebruiken:

(afdrukken ($1^2)+1 )

Als u een beetje experimenteert, zult u merken dat als een variabele geen geldig getal bevat, awk die variabele als een numerieke nul zal behandelen bij het evalueren van een wiskundige uitdrukking.

Veel exploitanten

Een ander leuk kenmerk van awk is de complete set wiskundige operatoren. Naast de standaard optelling, aftrekking, vermenigvuldiging en deling geeft awk ons ​​de mogelijkheid om de eerder gedemonstreerde exponentoperator "^", de restoperator "%" voor gehele deling en vele andere handige toewijzingsoperatoren te gebruiken die zijn geleend van C.

Deze omvatten pre- en post-incrementele/decrementele toewijzingsoperatoren (i++, --foo), toewijzingsoperatoren met optellen/aftrekken/vermenigvuldigen/delen (a+=3, b*=2, c/=2.2, d-=6.2) . Maar dat is nog niet alles: we hebben ook handige toewijzingsoperatoren voor de berekening van de rest van deling van gehele getallen en machtsverheffen (a^=2, b%=4).

Veldscheidingstekens

awk heeft zijn eigen set speciale variabelen. Met sommige kunt u nauwkeurig afstemmen hoe awk werkt, terwijl andere waardevolle invoerinformatie bevatten. We hebben al een van deze speciale variabelen besproken, FS. Zoals eerder vermeld, kunt u met deze variabele de reeks tekens opgeven die awk als veldscheidingsteken zal beschouwen. Toen we /etc/passwd als invoer gebruikten, was FS ingesteld op eerder. Dit bleek voldoende, maar FS geeft ons nog meer flexibiliteit.

De waarde van de FS-variabele hoeft niet uit één teken te bestaan; er kan een reguliere expressie aan worden toegewezen die een tekenpatroon van elke lengte specificeert. Als u velden verwerkt die zijn gescheiden door een of meer tabtekens, moet FS als volgt worden geconfigureerd:

FS="\t+"

Hierboven gebruikten we speciaal karakter reguliere expressie "+" wat betekent "een of meer exemplaren van het vorige teken".

Als de velden gescheiden zijn door witruimte (een of meer spaties of tabs), wilt u wellicht de FS instellen op de volgende reguliere expressie:

FS="[[:spatie:]+]"

Hoewel deze opstelling werkt, is dit niet nodig. Waarom? Omdat de standaardwaarde van FS één spatie is, wat awk interpreteert als "een of meer spaties of tabs". In ons specifieke voorbeeld is de standaard FS-waarde precies wat we nodig hadden!

Er zijn ook geen problemen met complexe reguliere expressies. Zelfs als de records worden gescheiden door het woord 'foo' gevolgd door drie cijfers, zal de volgende reguliere expressie de gegevens correct parseren:

FS = "foe"

Aantal velden

De volgende twee variabelen waar we naar gaan kijken, zijn doorgaans niet bedoeld om naar te schrijven, maar worden eerder gebruikt om nuttige informatie over de invoer te lezen en te verkrijgen. De eerste hiervan is de NF-variabele, ook wel het aantal velden genoemd. Awk stelt de waarde van deze variabele automatisch in op het aantal velden in het huidige record. U kunt de NF-variabele gebruiken om alleen bepaalde invoerregels weer te geven:

NF == 3 ( print "er zijn drie velden in dit item: " $0 )

Uiteraard kan de NF-variabele ook worden gebruikt in voorwaardelijke instructies, bijvoorbeeld:

( if (NF > 2) ( print $1 " " $2 " $3 ) )

Recordaantal

Een andere handige variabele is het recordnummer (NR). Het bevat altijd het nummer van het huidige record (awk beschouwt het eerste record als recordnummer 1). Tot nu toe hebben we ermee te maken gehad invoerbestanden, die één item per regel bevatte. In dergelijke situaties zal NR ook het huidige lijnnummer rapporteren. Wanneer we echter in latere artikelen in deze serie meerregelige records gaan hanteren, zal dit niet langer het geval zijn, dus voorzichtigheid is geboden! NR kan net als de NF-variabele alleen voor uitvoer worden gebruikt bepaalde lijnen invoer:

(NR< 10) || (NR >100) (print "We staan ​​op recordnummer 1-9 of 101 of meer")

Nog een voorbeeld:

( #skip header if (NR > 10) ( print "nu komt de echte informatie!" ) )

Awk biedt aanvullende variabelen die voor verschillende doeleinden kunnen worden gebruikt. We zullen deze variabelen in toekomstige artikelen bekijken. We zijn aan het einde gekomen van onze eerste verkenning van awk. In toekomstige artikelen in de serie zal ik meer geavanceerde awk-functionaliteit laten zien, en we zullen deze serie afsluiten met een real-world awk-applicatie. Als u in de tussentijd meer wilt weten, kunt u de onderstaande bronnen raadplegen.