--- title: Intro a Awk. tags: DAM, Big Data --- <div style="width: 30%; margin-left: auto;"> ![](https://hackmd.io/_uploads/HJiR4eGJT.png) </div> <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Llicència de Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a><br />Aquesta obra està subjecta a una llicència de <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Reconeixement-CompartirIgual 4.0 Internacional de Creative Commons</a> # Intro a Awk. ![imagen](https://hackmd.io/_uploads/r1UuuvPM0.png) Quan treballem amb informació emmagatzemada de manera semiestructurada en fitxers de text és molt comú haber de realitzar operacions de preparació i filtratge de les dades prèvies al seu procéssament. Si treballem sovint amb el mateix format, és molt útil disposar d'un script de pre-processat que realitzi aquesta tasca de preparació, però si necessitem realizar aquest tipus de tasques una única vegada, normalment és encara més ràpid i pràctic fer ús d'eines standar de processament de dades. Una opció comuna és l'ús de fulls de càlcul, tot i que no sempre és la més pràctica, però normalment és una eina amb la que molta gent es sent còmoda. Una molt bona alternativa és utilitzar la comanda de consola '__awk__', o la seva versió GNU: '**gawk**'. ## Què és Awk? Awk és una utilitat de consola, que es podria arribar a considerar fins i tot un llenguatge de programació orientat al tractament de text. Gairabé tots els sistemes operatius existents disposen d'una versió d'awk. Va ser creada a la dècada dels 70's als [laboratoris Bell](https://en.wikipedia.org/wiki/Bell_Labs). La funció principal d'awk és processar fitxers de text línia per línia, buscant patrons específics i executant accions determinades quan aquests patrons són trobats. És especialment útil per a tasques com extracció de dades, transformació de formats, generació de informes, etc. El funcionament bàsic d'awk es basa en regles. Cada regla consisteix en un patró i una acció associada. > patrò {acció} L'awk llegeix el fitxer de text línia per línia i compara cada línia amb els patrons definits a les regles. Quan troba una coincidència, executa l'acció associada a aquest patró. Per exemple, una regla senzilla podria ser: ```bash= awk '1 { print }' fitxer_de_text ``` L'exemple anterior és bàsicament inútil, però il·lustra el funcionament i la sintaxi de les regles awk. '1' és una constant amb valor diferent de zero. { print } és l'acció a realitzar, que per una altra banda, print és també l'acció per defecte a realitzar quan no s'especifica una acció. Per tant, el nostre exemple recorrerà tot el fitxer de text línia per línia i el mostrarà per pantalla. ## Exemples d'ús. Podem trobar una bona explicació amb exemples d'ús al següent link: [https://linuxhandbook.com/awk-command-tutorial/](https://linuxhandbook.com/awk-command-tutorial/) ## Fitxer d'exemple ```= DNI#APE_1#APE_2#NOM#DIA#MES#ANO#F_NAC#EDAD#DEPART#C_CONDUCIR#N_HIJOS#ESTADO_CIVIL#SUELDO 45.302.157-T#HAANAN#MIMUN#NADIA#7#1#1990#32880#26#INFORMATICA#NO#5#DIVORCIADO#1743 43.094.614-R#ABAD#BARRIO#FUENCISLA#9#8#1968#25059#47#SERVICIO TÉCNICO#NO#2#SOLTERO#2044 32.843.994-V#ABAD#VÁZQUEZ#TATIANA PALOMA#10#1#1962#22656#54#INFORMATICA#SI#1#SOLTERO#2774 13.138.768-H#ABAJO#HERNANDO#INMACULADA#26#9#1957#21089#58#RRHH#NO#5#SOLTERO#912 71.555.763-A#ABANADES#FERNÁNDEZ#LIBERTAD#16#11#1989#32828#26#RRHH#NO#2#SOLTERO#2095 45.293.901-R#ABDELKADER#MIMUN#KARIMA#7#11#1985#31358#30#SERVICIO TÉCNICO#NO#2#DIVORCIADO#2243 45.283.008-X#ABDEL-LAH#MOHAMED#CANDILA#23#3#1983#30398#32#CONTABILIDAD#SI#4#SOLTERO#2026 45.281.108-L#ABDERRAMAN#MOHAMED#LEILA#10#8#1982#30173#33#I+D#NO#2#DIVORCIADO#2520 12.383.679-L#ABRIL#FERNÁNDEZ#ELENA#21#10#1936#13444#79#RRHH#SI#5#CASADO#2488 71.547.481-R#ACEDO#PEREZ#ADORALINA#20#5#1959#19499#62#RRHH#SI#3#DIVORCIADO#1413 45.282.525-X#ACOSTA#BERNABE#M. ANGELES#24#10#1035#13081#80#INVESTIGACIÓN#NO#1#VIUDO#1555 80.159.645-E#ACOSTA#FERNÁNDEZ#MARIA#5#2#1947#17203#69#INVESTIGACIÓN#NO#1#SOLTERO#2066 51.929.697-K#ACOSTA#GONZALEZ#M. CARMEN#7#11#1977#28436#38#I+D#SI#1#SOLTERO#3049 06.243.735-V#ADEVA#RODRÍGUEZ#AFRICA#1#11#1936#13455#79#RRHH#NO#3#CASADO#973 53.567.648-G#AGRO-MARTÍN#JUAREZ#LUZDIVINA#21#11#1955#20414#60#RRHH#NO#3#DIVORCIADO#2666 12.760.394-V#AGUADO#SAN MARTÍN#JUAN CARLOS#11#7#1970#25760#45#CONTABILIDAD#SI#5#SOLTERO#2690 46.451.841-Y#AGUAYO#FERRERAS#DAMARIS#22#10#1980#29516#35#RRHH#NO#4#VIUDO#1303 44.097.101-Y#AGUDELO#LONDOÑO#NEILA ESLEIDI#27#3#1954#19810#61#RRHH#NO#5#SOLTERO#1778 ``` ## Ús de variables. Awk permet l'ús de variables per a emmagatzemar dades. Les variables no necessiten ser declarades, i el tipus s'infereix directament del valor que hi assignem. ```awk= variable_numerica = 10; variable_text = "Hola, món!"; ``` Totes les variables a Awk tenen un àmbit global i són accessibles des de qualsevol part de l'script. ### Variables pròpies d'Awk. Awk té un conjunt de variables predefinides: * __RS__ [separador de registres / **R**ecord **S**eparator]: Per defecte és el separador de línia, però el podem canviar per qualsevol altre caracter. * __FS__ [separador de camps / **F**ield **S**eparator]: Awk utilitza l'espai en blanc com a separador de camps per defecte. * __OFS__ [separador de camps a la sortida / **O**utput **F**ield **S**eparator]: És el caracter que utilitzarà Awk per separar els diferents camps quan els imprimeis per la sortida. * __NF__ [nombre de camps / **N**umber of **F**ields]: El nombre de camps que conté el registre actual. Si el separador és l'espai en blanc coincidirà amb el nombre de paraules de la línia actual. * __NR__ [posició del registre actual /**N**umber of **R**ecord]: Si mantenim el separador de registres per defecte, coincideix amb el nombre de la línia actual. ### Exemple d'ús de variables. 1. Comptar les paraules d'un fitxer de text: ```text= La casa que vull, que la mar la vegi i uns arbres amb fruit que me la festegin. Que hi dugui un camí lluent de rosada, no molt lluny dels pins que la pluja amainen. Per si em cal repòs que la lluna hi vingui; i quan surti el sol que el bon dia em digui. Que al temps de l’estiu niui l’oreneta al blanc de calç ric del porxo amb abelles. Oint la cançó del pagès que cava; amb la salabror de la marinada. Que es guaiti ciutat des de la finestra, i es sentin els clams de guerra o de festa: per ser-hi tot prest si arriba una gesta. ``` ```bash= xavi@portatil:~$ awk ' { paraules += NF } END { print "Nombre de paraules:", paraules } ' lacasaquevull.txt Nombre de paraules: 111 ``` 2. Comptar el nombre de línies d'un fitxer de text: ```bash= xavi@portatil:~$ awk ' END { print "Nombre de línies:", NR } ' lacasaquevull.txt Nombre de línies: 31 ``` 3. Comptar el nombre de paraules de cada línia i el nombre de paraules total: ```bash= xavi@portatil:~$ awk ' { paraules += NF; print NR, ":", NF} END { print "Nombre de paraules:", paraules } ' lacasaquevull.txt 1 : 4 2 : 5 3 : 5 4 : 4 5 : 0 ... 26 : 4 27 : 4 28 : 5 29 : 5 30 : 4 31 : 4 Nombre de paraules: 111 ``` ## Estructures de control. Awk també permet l'ús d'estructures de control: condicionals i bucles. ### Condicionals. La sintaxi dels condicionals és: > if (condició) {instruccions si True} else {instruccions si False} ### Bucles. Awk incorpora els bucles, __for__ i __while__ com a estructures de control amb la següent sintaxi: #### Bucle for La sintaxi del bucle for és similara la que utilitzem amb Java: ```C= for (inicializacio; condicio; increment/decrement) accio ``` ##### exemple: ```bash= $ awk 'BEGIN { for (i = 1; i <= 5; ++i) print i }' 1 2 3 4 5 ``` #### Bucle while: ```C= do action while (condition) ``` ##### exemple: ```bash= $ awk 'BEGIN {i = 1; do { print i; ++i } while (i < 6) }' 1 2 3 4 5 ``` ### Arrays Awk anomena __array__ el que en PHP diem __associative array__ o en Python __dictionary__. Aquest tipus d'estructura no és una sèrie ordenada d'elements, és un conjunt de parelles clau-valor, i a Awk té la restricció de que la clau sempre ha de ser una cadena de text. Per recòrrer tots els elements d'un array podem utilitzar un bucle for amb la següent sintaxi: ```= for (var in arrayname) actions ``` Imaginem que tenim un log d'un proxy web organitzat en 4 columnes: data, hora, adreça IP i nombre de pàgines web a les que ha accedit cada IP: ```bash= $ cat Iplogs.txt 180607 093423 123.12.23.122 133 180607 121234 125.25.45.221 153 190607 084849 202.178.23.4 44 190607 084859 164.78.22.64 12 200607 012312 202.188.3.2 13 210607 084849 202.178.23.4 34 210607 121435 202.178.23.4 32 210607 132423 202.188.3.2 167 ``` Com podem veure cada IP pot aparèixer més d'una vegada dins del nostre log. Podem utilitzar __awk__ per a comptar quantes vegades apareix cada IP: ```bash= $ awk '{ > Ip[$3]++; > } > END{ > for (var in Ip) > print var, "access", Ip[var]," times" > } > ' Iplogs.txt ``` ```= 164.78.22.64 access 1 times 123.12.23.122 access 1 times 202.178.23.4 access 3 times 202.188.3.2 access 2 times 125.25.45.221 access 1 times ``` Podem complicar una mica més l'exemple mostrat a dalt per a indicar també el nombre total de pàgines web a les que accedeix cada màquina. En aquesta ocasió he desat les regles awk en un script anomenat _'script.awk'_ per a poder-lo reaprofitar en ocasions posteriors: ```bash= $cat script.awk BEGIN { print "IP Address\tAccess Count\tNumber of sites"; } { Ip[$3]++; count[$3]+=$NF; } END{ for (var in Ip) print var,"\t",Ip[var],"\t\t",count[var]; } $ awk -f script.awk Iplogs.txt IP Address Access Count Number of sites 164.78.22.64 1 12 123.12.23.122 1 133 202.178.23.4 3 110 202.188.3.2 2 180 125.25.45.221 1 153 ``` ### Bones pràctiques * Defineix sempre el separador de camps (-F) si el fitxer no està separat per espais. * Usa BEGIN per inicialitzar encapçalaments o variables. * Usa END per fer resums o informes finals. * Desa scripts awk en fitxers .awk per reutilitzar-los. * Prova expressions amb dades simples abans d’aplicar-les a fitxers grans. ### Resum ràpid de sintaxi |Objectiu| Comanda| |---|---| | Mostrar 2a columna | awk '{print $2}' fitxer| | Filtrar línies amb “ERROR” | awk '/ERROR/' fitxer| | Comptar línies | awk 'END{print NR}' fitxer| | Sumar columna 3 | awk '{sum+=$3} END{print sum}' fitxer| | Canviar separador | awk -F"," '{print $1,$2}' fitxer.csv| ### Opcions de línia de comandes útils |Opció | Descripció | |---|---| |-F | Defineix separador de camps | |-v var=value | Defineix variables abans d’executar Awk | |-f script.awk | Executa un script | |--dump-variables | Mostra variables internes (GNU awk) | |--lint | Comprova errors d’estil o compatibilitat |