Laat awk zoeken naar vergelijkingen van meer dan 3. AWK: Voorbeeldprogramma's. Functies op systeemniveau

Tekst is het hart van Unix. De 'alles is een bestand'-filosofie is doordrenkt in het hele systeem en de tools die daarvoor zijn ontwikkeld. Daarom werken
met tekst is een van de vereiste vaardigheden systeembeheerder of een beginnende Linux-gebruiker.

AWK is een van de meest krachtige hulpmiddelen voor het verwerken en filteren van tekst, zelfs toegankelijk voor mensen die niets met programmeren te maken hebben.

Gebruik awk op Linux

De eenvoudigste en meest gebruikte taak is het ophalen van velden uit de standaarduitvoer. U zult geen beter hulpmiddel voor deze taak vinden dan awk. Standaard scheidt awk velden met spaties. Als u het eerste veld wilt afdrukken, hoeft u alleen maar de parameter $1 op te geven:

echo "één twee drie vier" | awk "(afdruk $ 1)"

Ja, het gebruik van accolades is een beetje ongebruikelijk, maar dit is pas de eerste keer. Heeft u al geraden hoe u het tweede, derde, vierde of andere veld moet afdrukken? Correct, het is respectievelijk $2, $3, $4.

echo "één twee drie vier" | awk "(afdruk $ 3)"

Soms is het nodig om gegevens in een specifiek formaat te presenteren, bijvoorbeeld door een paar woorden te selecteren. AWK kan gemakkelijk meerdere velden groeperen en u kunt zelfs statische gegevens opnemen:

echo "één twee drie vier" | awk "(print $3,$1)"
drie één

echo "één twee drie vier" | awk "(print "foo:",$3,"| bar:",$1)"
foo: drie | balk: één

Als de velden worden gescheiden door een ander scheidingsteken dan spaties, geeft u eenvoudigweg het gewenste scheidingsteken op tussen aanhalingstekens in de parameter -F, bijvoorbeeld :: : :

echo "één mississippi: twee mississippi: drie mississippi: vier mississippi" | awk -F: "(print $4)"
vier mississippi

Maar het scheidingsteken hoeft niet tussen aanhalingstekens te staan. Volgende uitvoer vergelijkbaar met de vorige:

echo "één mississippi: twee mississippi: drie mississippi: vier mississippi" | awk -F: "(print $4)"
vier mississippi

Soms moet u gegevens verwerken met een onbekend aantal velden. Als u het laatste veld moet selecteren, kunt u de variabele $NF gebruiken. Zo kunt u het laatste veld weergeven:

echo "één twee drie vier" | awk "(print $NF)"
vier

U kunt ook de $NF-variabele gebruiken om het voorlaatste veld te verkrijgen:

echo "één twee drie vier" | awk "(print $(NF-1))"
drie

Of velden vanuit het midden:

echo "één twee drie vier" | awk "(print $((NF/2)+1))"
drie

echo "één twee drie vier vijf" | awk "(print $((NF/2)+1))"
drie

Dit alles kan worden gedaan met behulp van hulpprogramma's zoals sed, cut en grep, maar het zal veel moeilijker zijn.

En nog een kenmerk van awk, ondersteuning voor handlers voor strings:

echo -e "één 1\n twee 2" | awk "(afdruk $ 1)"
een
twee

echo -e "één 1\n twee 2" | awk "(afdruk $ 2)"
1
2

echo -e "één 1\n twee 2" | awk "(som+=$2) END (afdruksom)"
3

Dit betekent dat we voor elke regel het volgende codeblok moeten uitvoeren. Dit kan bijvoorbeeld worden gebruikt om de hoeveelheid overgedragen gegevens te tellen voor verzoeken uit het webserverlogboek.

Stel je voor dat we een toegangslogboek hebben dat er als volgt uitziet:

23 juli 18:57:12 HTTPD: "GET/foo/bar HTTP/1.1" 200.344
23 juli 18:57:13 HTTPD: "GET/HTTP/1.1" 200 9300
23 juli 19:01:27 HTTPD: "GET / HTTP / 1.1" 200 9300
23 juli 19:01:55 HTTPD: "GET / Foo / Baz HTTP / 1.1" 200 6401
23 juli 19:02:31 HTTPD: "? GET / Foo / Baz-pagina = 2 HTTP / 1.1" 200 6312

We weten dat het laatste veld het aantal overgedragen bytes is, en dan kunnen we de variabele $NF gebruiken:

< requests.log awk "{print $NF}"
344
9300
9300

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. Het awk-programma draait meestal in de 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 het commando wordt weggelaten, komt dit overeen met opgeven (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 hele bestand is voltooid.

AWK heeft ook nog veel andere speciale waarden die je 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.

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

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 een willekeurig getal genereren in 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

, of 0 als dit niet voorkomt.`

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 'positie in' terug geeft de positie 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

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 indeling is gemaakt volgens 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 waarin de reguliere expressie voorkomt 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 RSTART 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). 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:

Waarden = "1"; waarden afdrukken; waarden = "één"; waarden afdrukken;

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: Houd er rekening mee 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) staat, die in in dit geval is een scriptargument, terwijl $1 deel uitmaakt van de $1-syntaxis (dat wil zeggen het eerste veld op de regel).

#!/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

Daniel Robbins, president/CEO, Gentoo Technologies, Inc.

Beschrijving: Awk is een prachtige taal met een heel vreemde naam. In dit eerste artikel van een driedelige serie geeft Daniel Robbins een korte introductie tot de basisprincipes van awk-programmeren. Toekomstige artikelen in de serie zullen meer geavanceerde onderwerpen behandelen, eindigend met een solide, real-world awk demo-applicatie.

Dit artikel tags: awk

Markeer dit!

Datum: 29-01-2009

Moeilijkheidsgraad: eenvoudig

Opmerkingen: 0 (Bekijken | Een opmerking toevoegen - Inloggen)

Beoordeel dit artikel

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 het volgende commando 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 worden accolades gebruikt om tekstblokken te groeperen, net als in C. In ons tekstblok is er slechts één afdrukcommando. In awk drukt het printcommando zonder enige aanvullende parameters de volledige inhoud van de huidige regel af.

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 "hiya" )" /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 zal een lijst afdrukken van alle accounts op het systeem: $ /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

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 het volgende commando 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. Natuurlijk kunnen we indien nodig ook veldlabels invoegen: $ awk -F: "( print "gebruikersnaam: " $1 "\t\tuid: " $3" )" /etc/passwd

Als resultaat krijgen we de volgende uitvoer: gebruikersnaam: halt uid:7

gebruikersnaam: operator uid:11

gebruikersnaam: root-uid:0

gebruikersnaam: shutdown-uid:6

gebruikersnaam: synchronisatie-uid:5

gebruikersnaam: bin uid:1

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 myscript.awk myfile.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 (

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 verwerken invoerbestand, is dit een geweldige 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:/foo/ ( print ) bevatten

Natuurlijk kunt u complexere reguliere expressies gebruiken. Hier is een script dat alleen regels afdrukt die een float bevatten: /+\.*/ ( print )

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 afgedrukt als het vijfde veld van dezelfde regel de tekenreeks root:$5 ~ /root/ ( print $3 ) bevat

Voorwaardelijke uitspraken

Awk biedt ook hele mooie C-achtige if-statements. Indien gewenst kunt u het vorige script herschrijven met if:(

if ($5 ~ /root/) (

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, zien de if-instructies er, zelfs met complexe geneste conditionele waarden, identiek uit aan hun C-tegenhangers:(

if ($1 == "foo") (

if ($2 == "foo") (

) else if ($1 == "bar") (

Met behulp van if-instructies kunnen we deze code transformeren: ! /matchme/ (afdrukken $1 $3 $4 )

zoals dit: (

if ($0 !~ /matchme/) (

Beide scripts drukken alleen regels af die niet de matchme-tekenreeks bevatten. 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") ( print )

In dit voorbeeld worden alleen rijen uitgevoerd waarin het eerste veld foo is en het tweede veld 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 )

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 het uiteindelijke totaal af, wat aangeeft het aantal gevonden lege regels.

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. Bekijk dit voorbeeld om te zien wat ik bedoel: x = "1.01"

# We hebben ervoor gezorgd dat x de *string* "1.01" bevat

# We hebben zojuist 1 toegevoegd aan *string*

# Dit is trouwens een reactie :)

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:( print ($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 hebben we het speciale reguliere expressieteken "+" gebruikt, wat betekent "een of meer exemplaren van het vorige teken".

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

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="foo"

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 deze invoer: " $0 )

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

als (NF > 2) (

afdrukken $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 te maken gehad met invoerbestanden die één record per regel bevatten. 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:(

als (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.

Hoofddoel van het programma akelig- interpretatie van de programmeertaal awk, waarmee u snel een programma kunt maken voor het analyseren en converteren van tekstbestanden. Een typisch awk-script ziet er als volgt uit:

sjabloon1 (actie1) sjabloon2 (actie2) ...

Het programma leest het invoerbestand of de stream en ontleedt elke regel op een patroonovereenkomst. Als er een match is, worden de acties beschreven in krullende beugels. Als het patroon ontbreekt, wordt de actie op elke lijn toegepast. Als er geen actie plaatsvindt, wordt de regel afgedrukt standaard uitvoer. Een actie kan bestaan ​​uit een reeks instructies, gescheiden door puntkomma's.

Het derde veld (kolom, woord) van elke regel afdrukken:

Code:

ls -l | awk "(afdrukken($3))"

Drukt de twee opgegeven velden van elke regel af:

Code:

ls -l | awk "(afdrukken($9,$1))"

Velden met spaties afdrukken:

Code:

ls -l | awk "(print($9," ",$1))"

Gebruik de optie -F om een ​​ander veldscheidingsteken dan een spatie op te geven. In dit geval is het veldscheidingsteken een dubbele punt:

Code:

Awk -F: "(print($2))" $filenameForProcessing

Om een ​​awk-script te gebruiken dat in een bestand is opgeslagen:

Code:

Awk -f $scriptBestandsnaam $bestandsnaamForProcessing

Het awk-scriptbestand kan uitvoerbaar worden gemaakt en de bijbehorende sha-bang daarin worden gespecificeerd. Zo'n script zal een bestand als parameter gebruiken voor verwerking:

Code:

#!/usr/bin/awk -f

awk-variabelen worden gemaakt wanneer ze voor de eerste keer worden geopend en kunnen gehele getallen, floats of strings bevatten, zoals bepaald door de context. De speciale variabele RS slaat de waarde van het recordscheidingsteken op (standaard\n), en de variabele FS slaat de waarde van het veldscheidingsteken op (standaard - spatie). Als een van deze variabelen meer dan één teken bevat, wordt die waarde geïnterpreteerd als een reguliere expressie. De awk-taal bevat een aantal ingebouwde string- en wiskundige functies, voorwaardelijke uitspraken en looping-operators, ondersteunt arrays en definitie aangepaste functies. Je kunt het op internet vinden uitgebreide gidsen in de awk-taal, evenals automatische vertalers (“vertalers”) van awk-scripts naar andere talen (bijvoorbeeld C of Perl).

Speciale soorten patronen zijn BEGIN en END. Ze worden niet gecontroleerd aan de hand van invoerstroominvoer. De patroonactie BEGIN wordt één keer uitgevoerd voordat de invoergegevens zijn gelezen, en de patroonactie END wordt één keer uitgevoerd nadat de invoergegevens zijn gelezen.

Een voorbeeld van het verwijderen van aangrenzende dubbele regels in een bestand:

Code:

Bestandsnaam=test.txt
res=`awk "BEGIN (PREV="") (if ($0 != PREV) (print $0; PREV=$0))" $bestandsnaam`
echo "$res" > $bestandsnaam

Voordat we beginnen, stellen we de PREV-variabele in op lege regel. De rest van het awk-script heeft geen sjabloon en wordt daarom op elke string uitgevoerd. Als huidige lijn niet gelijk is aan PREV, wordt uitgevoerd en vervolgens naar de PREV-variabele geschreven. Als dus bij het verwerken van elke regel deze gelijk blijkt te zijn aan de vorige, wordt deze niet afgedrukt.

Voorbeeld van veldaaneenschakeling:

Code:

Awk "(a = $3 $4; print a)" $bestandsnaam

Voorbeeld van het optellen van veldwaarden:

Code:

Awk "(a = $3+$4; print a)" $bestandsnaam

Het concept van "selector" moet worden opgevat als een uitbreiding van het concept van "sjabloon". Waar een patroon is opgegeven in de opdrachtstructuur, kan in het algemeen elke selector verschijnen.

Het derde veld testen met een reguliere expressie en de hele regel afdrukken als dit lukt:

Code:

Awk "$3~/(7)$/ (print)" $bestandsnaam

Controleert het eerste veld op gelijkheid met de opgegeven tekenreeks en drukt het tweede veld af als dit lukt.

04.10.2015
16:55

Het awk-hulpprogramma is een voorbeeld van een klassieker Linux-applicaties voor tekstverwerking. Het is zeer veelzijdig en effectief, hoewel het geen volwaardige programmeertaal biedt. U kunt er echter zeker van zijn dat de mogelijkheden ervan ruim voldoende zijn om veel problemen op te lossen. geautomatiseerde verwerking tekst (vooral in combinatie met andere consolehulpprogramma's).

Manieren om awk-programma's uit te voeren

Als het awk-programma vrij eenvoudig en kort is, kan de code rechtstreeks in de console worden getypt:

Akelig"< код awk-программы >" < имя_файла_для_обработки >

Je kunt meer dan alleen awk als invoer gebruiken: tekstbestanden, maar ook de uitvoer in standaard stroom andere toepassingen:

< некое_приложение >| akelig"< код awk-программы >"

In het geval dat de code akke programma's voldoende volumineus of moet bewaard blijven hergebruiken, kan het worden aangeroepen vanuit een bestand met de schakeloptie -f:

Awk-f< имя_файла_с_кодом_awk_программы > < имя_файла_для_обработки >

Om experimenten uit te voeren gebruiken we het bestand test.cpp, waarop we de resultaten van de awk-programma's zullen controleren:

#erbij betrekken #erbij betrekken #erbij betrekken ongeldige test1(); int-test2(); // C-stijl commentaar voor main() functie int main(int argc, char** argv) ( std::cout<< "Hello, world!" << std::endl; for(int i = 0; i < 10; ++i) { std::cout << i << std::endl; } return 0; } // Комментарий в стиле С для функции test1() void test1() { std::cout << "Hello, test1!" << std::endl; } // Комментарий в стиле С для функции test2() int test2() { std::cout << "Hello, test2!" << std::endl; }

Reclame

Tekenreeksen filteren met awk

Allereerst kunt u met awk regels uit tekst selecteren op basis van reguliere expressies en enkele numerieke voorwaarden.

Tekenreeksen selecteren die overeenkomen met een reguliere expressie

Om bijvoorbeeld alle regels in het bestand test.cpp op te halen die de preprocessorrichtlijn #include bevatten, gebruiken we de volgende opdracht:

Awk "/^#\s*include/" test.cpp

De reguliere expressie wordt tussen twee / tekens geschreven. Als resultaat krijgen we:

#erbij betrekken #erbij betrekken #erbij betrekken

Tekenreeksen selecteren die NIET overeenkomen met een reguliere expressie

Om alle regels weg te laten die niet overeenkomen met de reguliere expressie, gebruikt u de opdracht uit de vorige subsectie en voegt u een uitroepteken toe aan het begin van de awk-code. Op deze manier sluiten we bijvoorbeeld alle commentaarregels uit:

Awk "! /^[/](2).*/" test.cpp

Dit is wat er nog over is:

#erbij betrekken #erbij betrekken #erbij betrekken ongeldige test1(); int-test2(); int main(int argc, char** argv) ( std::cout<< "Hello, world!" << std::endl; for(int i = 0; i < 10; ++i) { std::cout << i << std::endl; } return 0; } void test1() { std::cout << "Hello, test1!" << std::endl; } int test2() { std::cout << "Hello, test2!" << std::endl; }

Rijen uit een bepaald bereik selecteren

U kunt een reeks tekenreeksen definiëren die op het scherm moeten worden weergegeven met behulp van twee reguliere expressies, gescheiden door komma's. Laten we als voorbeeld de definitie zoeken van alle functies die int retourneren:

Awk "/^int .*(.*) (/, /^)/" test.cpp

Relevant resultaat:

Int main(int argc, char** argv) ( std::cout<< "Hello, world!" << std::endl; for(int i = 0; i < 10; ++i) { std::cout << i << std::endl; } return 0; } int test2() { std::cout << "Hello, test2!" << std::endl; }

Combineren van filteromstandigheden

Als u tekenreeksen tegelijk wilt controleren op verschillende voorwaarden, gebruikt u de operatoren && (AND) en ||. (OF) .

Met de volgende opdracht worden alle opmerkingen afgedrukt die geen hoofdtekst bevatten:

Awk "/[/](2).*/ && ! /main/" test.cpp

Als resultaat hebben we:

// C-stijl commentaar voor de test1() functie // C-stijl commentaar voor de test2() functie

Voorheen zochten we naar een reeks regels met behulp van twee reguliere expressies, maar als de regelnummers die moeten worden uitgevoerd vooraf bekend zijn, wordt alles vereenvoudigd:

Afijn "4< NR && NR < 7" test.cpp

NR is een awk-variabele die het regelnummer specificeert. De gepresenteerde code voert dus de 5e en 6e regel uit:

Ongeldige test1(); int-test2();

Selectie van regels op basis van voorwaarden met betrekking tot individuele woorden

Awk kan tekst niet alleen op regels filteren, maar ook op individuele woorden. Er kan naar het i-de woord in een regel verwezen worden met $i. De nummering begint bij één en $0 definieert de inhoud van de hele regel. Het aantal woorden in een regel wordt bepaald met behulp van de NF-variabele, dus $NF verwijst naar het laatste woord. Laten we bijvoorbeeld tekenreeksen zoeken waarvan het eerste woord int of void is:

Awk "$1 == "int" || $1 == "void"" test.cpp

Overeenkomstige console-uitvoer:

Ongeldige test1(); int-test2(); int main(int argc, char** argv) ( void test1() ( int test2() (

Het is echter eenvoudiger om een ​​reguliere expressiecontrole voor een woord te gebruiken. Om dit te doen, biedt awk een speciale operator ~, die tussen de variabele die naar het woord verwijst, en de reguliere expressie moet worden geplaatst. Laten we als voorbeeld de vorige opdracht in een compactere vorm herschrijven:

Awk "$1 ~ /int|void/" test.cpp

Selecteer rijen op basis van numerieke kenmerken

Rekenkundige operatoren in de C-taal zijn beschikbaar in awk, wat u bewegingsvrijheid geeft. In het onderstaande voorbeeld worden alle even regels afgedrukt (NR is het regelnummer):

Awk "NR % 2 == 0" test.cpp

Relevante uitvoer:

#erbij betrekken int-test2(); // C-stijlcommentaar voor de functie main() std::cout<< "Hello, world!" << std::endl; for(int i = 0; i < 10; ++i) { } return 0; void test1() { } // Комментарий в стиле С для функции test2() std::cout << "Hello, test2!" << std::endl;

Het volgende awk-programma drukt alle regels af waarvan het eerste woord een lengte van drie heeft:

Awk "lengte($1) == 3" test.cpp

Als resultaat krijgen we:

Int-test2(); int main(int argc, char** argv) ( int test2() (

Awk "NF == 2" test.cpp

En de bijbehorende uitvoer:

#erbij betrekken #erbij betrekken #erbij betrekken ongeldige test1(); int-test2();

Reclame

retour 0;

Werken met strings in awk

Zoals je kunt zien, heeft awk een goede set functies voor het filteren van tekstreeksen. U kunt echter nog steeds verschillende transformaties op deze strings uitvoeren. Tekenreeksopdrachten moeten tussen accolades (...) worden geplaatst. De code tussen haakjes wordt opeenvolgend aangeroepen voor elke regel tekst die wordt verwerkt.

Geformatteerde uitvoer

Awk heeft een direct equivalent van de C-functie printf(). Laten we als voorbeeld het nummer aan het begin van elke regel afdrukken:

Awk "( printf "%-2d %s\n", NR, $0 )" test.cpp

Dit is wat we hebben: 1 #opnemen 2 #opnemen 3 #opnemen<< "Hello, world!" << std::endl; 11 12 for(int i = 0; i < 10; ++i) { 13 std::cout << i << std::endl; 14 } 15 16 return 0; 17 } 18 19 // Комментарий в стиле С для функции test1() 20 void test1() { 21 std::cout << "Hello, test1!" << std::endl; 22 } 23 24 // Комментарий в стиле С для функции test2() 25 int test2() { 26 std::cout << "Hello, test2!" << std::endl; 27 }

4 5 ongeldige test1(); 6 int-test2(); 7 8 // C-stijl commentaar voor de functie main() 9 int main(int argc, char** argv) ( 10 std::cout

Conversiefuncties

Naast printf() heeft awk nog andere functies. Bijvoorbeeld print() en toupper() :

Relevant resultaat:

Awk "( print toupper($0) )" test.cpp #ERBIJ BETREKKEN #ERBIJ BETREKKEN #ERBIJ BETREKKEN<< "HELLO, WORLD!" << STD::ENDL; FOR(INT I = 0; I < 10; ++I) { STD::COUT << I << STD::ENDL; } RETURN 0; } // КОММЕНТАРИЙ В СТИЛЕ С ДЛЯ ФУНКЦИИ TEST1() VOID TEST1() { STD::COUT << "HELLO, TEST1!" << STD::ENDL; } // КОММЕНТАРИЙ В СТИЛЕ С ДЛЯ ФУНКЦИИ TEST2() INT TEST2() { STD::COUT << "HELLO, TEST2!" << STD::ENDL; }

Voorwaardelijke voorwaarden

If-else-instructies zijn beschikbaar in awk-programma's. De volgende code wordt bijvoorbeeld afgedrukt zonder regels te wijzigen die int op de eerste positie hebben en ( op de laatste, anders wordt --- naar de console verzonden:

Awk " ( if($1 == "int" && $NF == "() print; else print "---" )" test.cpp

Het uitvoeren van de code levert de volgende uitvoer op:

Int main(int argc, char** argv) ( --- --- --- --- --- --- --- --- --- --- --- --- -- - --- --- int-test2() ( --- ---

Variabelen

Variabelen die niet eerst hoeven te worden gedeclareerd, zijn ook beschikbaar in awk-programma's. De volgende code voor het tellen van het aantal regels en woorden in de tekst wordt in het stat.awk-bestand geplaatst:

( lineCount++; wordCount += NF ) END ( printf "aantal regels: %d, aantal woorden: %d\n", lineCount, wordCount )

Dan heet het als volgt:

Awk -f stat.awk test.cpp

Uitvoeringsresultaat:

Aantal regels: 27, aantal woorden: 88

Het END-filter specificeert dat de code tussen haakjes erna pas mag worden uitgevoerd nadat alle regels zijn doorlopen. Het BEGIN-filter is ook beschikbaar in awk, dus in een algemener geval heeft het programma de volgende vorm:

BEGIN (opgeroepen voordat het doorlopen van rijen begint) (opgeroepen voor elke rij na het gedeelte BEGIN, maar vóór het gedeelte END) END (opgeroepen nadat het doorlopen van rijen is voltooid)

Wc -lw test.cpp

Cycli

In awk-programma's heb je ook toegang tot C-stijl for- en while-loops. Laten we bijvoorbeeld alle regels in omgekeerde volgorde afdrukken. Laten we een bestand reverse.awk maken met de volgende inhoud:

( for(i = NF; i > 0; --i) printf "%s ", $i; printf "\n" )

Laten we het programma als volgt aanroepen:

Awk -f reverse.awk-test.cpp

Als gevolg hiervan worden de woorden in elke regel in omgekeerde volgorde afgedrukt:

#erbij betrekken #erbij betrekken #includetest1(); ongeldige test2(); int main() functies voor C-stijl in Comment // () argv char** argc, int main(int std::endl;<< world!" "Hello, << std::cout {) ++i 10; < i 0; = i int for(std::endl; << i << std::cout } 0; return } test1() функции для С стиле в Комментарий // { test1() void std::endl; << test1!" "Hello, << std::cout } test2() функции для С стиле в Комментарий // { test2() int std::endl; << test2!" "Hello, << std::cout }

Niet-standaard woordscheidingsteken

Standaard gebruikt awk spaties als woordscheidingstekens, maar dit gedrag kan worden gewijzigd. Om dit te doen, gebruikt u de schakelaar -F, gevolgd door een lijn die het scheidingsteken definieert. Het volgende programma geeft bijvoorbeeld de naam van een groep en zijn gebruikers weer (als de groep gebruikers heeft) uit het bestand /etc/group, met een dubbele punt als scheidingsteken:

Awk -F: "( if($4) printf "%15s: %s\n", $1, $4 )" /etc/group

Filters en afdrukopdrachten combineren

Alle eerder besproken filters kunnen worden gebruikt in combinatie met tekenreeksverwerkingsopdrachten. Het is voldoende om de beperkingen vóór de accolades te schrijven. Hieronder ziet u een voorbeeld van het afdrukken van de eerste 9 regels van de uitvoer van het ps-commando, met informatie over de gebruiker, proces-ID en commandonaam:

Ps axu | oke "NR< 10 { print $1, $2, $NF }"

Na de lancering zullen we zien:

GEBRUIKER PID-OPDRACHT root 1 /sbin/init root 2 root 3 root 5 root 7 root 8 root 9 root 10