blog_hero_Een AWK-basis

Een AWK-basis

Aug 10 2023

Wat eerst?

Het commando awk (of gawk) is een supersterke tool die meegeleverd wordt met de meeste GNU/Linux distributies en een eerste versie bestaat al sinds 1977. De gawk is de GNU implementatie van de awk-tool. Als je een ls -alh $(which awk) uitvoert, merk je dat dat een symlink is naar de gawk executable.

Leuk weetje: de Naam AWK komt van de oorspronkelijke ontwikkelaars; Aho, Weinberger en Kernighan.

Deze tool ga je kunnen gebruiken voor tekst verwerking en manipulaties. Specifiek gaat awk lijnen, records of tekst zoeken die aan bepaalde patronen voldoen. De gefilterde content kunnen we dan uitvoeren met bepaalde manipulaties. Awk is eigenlijk ook een volledige programmeertaal waarmee je tal van functies in en buiten awk kan benutten voor wat je probeert te bereiken.

De alternatieven

Velen zullen ongetwijfel ook gekend zijn met grep en sed. Beide commando's zijn ook vaak beschikbaar en alhoewel ook stevige opties, kunnen ze niet zo veel als awk. Leer alle commando's sowieso kennen maar als je je specialiseert in één van de drie, kies dan zeker voor awk. De tool grep is voornamelijk gekend voor eenvoudig tekst in bestanden terug te vinden. Sed daarnaast is een specialist als stream editor om tekst eenvoudig aan te passen. Hou deze blog zeker in het oog als je ook van deze tools meer wilt weten.

# Zoek recursief naar TEKST en inverteer dan de selectie
grep -v -r "TEKST" /pad/naar/map/of/bestand
# zoek naar alle TEXT entries vanaf lijn 3 in een bestand en verander naar TEKST
sed -n `3,$ s/TEXT/TEKST/g` <bestand>

Belangrijk om te weten

AWK gebruikt een aantal automatische variabelen om je op weg te helpen. Dit zijn de voornaamste die je gaat willen gebruiken:

  • RS (Record Seperator): Standaard gaat AWK lijn per lijn willen verwerken. Dit gaat in vele gevallen ook telkens een nieuwe record aangeven. Als dat niet het geval is, kan je dit aanpassen naar een andere waarde.
  • NR (current input Number Record): Per record dat er verwerkt wordt, wordt er ook bijgehouden op welk record nummer we zitten. Dat nummer wordt opgeslagen in het NR. Je kan zo de even of onevenrecords filteren of misschien alleen de eerste lijn laten vallen.
  • FS/OFS ((Output) Field Seperator): Binnen een lijn of record zitten verschillende fields of data items. In een CSV-bestand worden die vaak gesplitst met een , of een ;-teken. De FS is dan het record waarmee AWK de input gaat splitsen. Achteraf bij het printen van de output, kan dat opnieuw gesplitst worden met de OFS-variabele.
  • NF (Number Field): Dit geeft aan hoeveel data items er in het huidige record bevinden. Dit kan handig zijn als FS nog steeds een standaard whitespace is. Zo weet je meteen hoeveel woorden er in het record zitten.

Een paar commando's

Het meest basis commando is: print alle records. Dat doen we zo:

awk '1 {print}' <bestand>
# Omdat dit de default actie is, kunnen we ook
awk 1 <bestand>

Omdat we de standaard actie print kennen, kunnen we deze weglaten en een paar complexere bewerkingen typen.

# Toon alles behalve de eerste lijn / record
awk 'NR>1' <bestand>
# Toon enkel de eerste lijn / record
awk 'NR==1' <bestand>
# Toon lijn / record 2 en 3
awk 'NR>1 && NR<4' <bestand>
# Verwijder alle witlijnen
awk 'NF' <bestand>

We kunnen ook zoeken op tekst en die tekst aanpassen:

# Zoek naar "TEKST"
awk '/TEKST/ {print $0}' <bestand>
# Vervang TEXT naar TEKST
awk '{gsub(/TEXT/,"TEKST")}{print}' <bestand>

Standaard gaat awk de output naar de terminal sturen. Maak daar gebruik van om jouw wijzigingen eerst te controleren. Achteraf kan je dit gemakkelijk doorsturen naar een onder bestand met > of >>. Wil je toch het originele bestand aanpassen gebruik dan awk -i inplace.

Wiskundige berekeningen zijn ook geen probleem. Lijnen of records tellen of de data zelf optellen? Dat doe je zo:

# Tel alle records van kolom 4 en tel dan enkel die waar waarin TEKST voorkomt
awk '{count[$4]++} END {print count["TEKST"]}' FS=, <bestand>
# Maak een som van alle waarde in de eerste kolom
awk '{ SOM=SOM+$1 } END {print SOM}' FS=; OFS=; <bestand>
# Als een waarde in de eerste kolom hoger is dan 9000, print dan die lijn
awk '{ if ($1 > 9000) {print $0} }' <bestand>

Met die laatste entries zie je al dat we richting een echte programmeertaal aan het gaan zijn. Nu gaan we nog een stapje verder met een for-lus. Als je niet vertrouwd bent met programeren, gaat deze data als een groupering of array beschouwen. Voor iedere aparte waarde, gaan we dan een bewerking doen. In dit geval optellen per naam.

awk `+$1 { CREDITS[$3]+=$1} END { for (NAAM in CREDITS) print NAAM, CREDITS[NAAM]}` FS=, <bestand>

Naast een for-lus, heb je ook if-else of switch-statements voor conditionele statements en while- en do-while-lussen voor iteratie.

Tot slot nog een ander leuk trukje is externe commando's oproepen en de output daarvan toevoegen aan jouw data. Hieronder halen we zo bijvoorbeeld de datum op door het date-commando uit te voeren. Dit gebruiken we om een hoofdding aan te maken om aan te geven wanneer het bestand aangepast is.

awk 'BEGIN { printf("AANGEPAST OP: "); system("date")} /^AANGEPAST OP:/ {next} 1' <bestand>

De volledige GAWK handleiding is vrij en gratis beschikbaar op de website van GNU.org.