Introducción a AWK (II)
Por dónde seguir
Este es el segundo post de una seria sobre AWK. El primero lo puedes encontrar aquí. Si no le has echado un vistazo te recomiendo hacerlo. Sigamos ahora por donde lo dejamos.

Operaciones matemáticas con varias columnas
Una de las últimas cosas que hicimos en el post anterior sobre AWK fue sumar una columna para ver el total. Lo cierto es que hay muchas más posibilidades. Teniendo el mismo archivo que antes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | $ cat inventario.txt Ene 13 25 15 115 Feb 15 32 24 226 Mar 15 24 34 228 Abr 31 52 63 420 May 16 34 29 208 Jun 31 42 75 492 Jul 24 34 67 436 Ago 15 34 47 316 Sep 13 55 37 277 Oct 29 54 68 525 Nov 20 87 82 577 Dic 17 35 61 401 Ene 21 36 64 620 Feb 26 58 80 652 Mar 24 75 70 495 Abr 21 70 74 514 |
Vamos ahora a sumar lo obtenido en los meses de Enero. Recordemos que la última columna se refiere a los euros que obtenemos ese mes.
Así que tendríamos que hacer algo como: “Si estamos en Enero entonces súmalo al total. Cuando terminemos con todos los ‘Eneros’ entonces imprimimos el resultado”. Esto en AWK sería:
1 | awk '$1=="Ene" { sum=sum+$5 }; END {print "Hemos ganado " sum "€ en total con todos los meses de Enero"}' inventario.txt |
Como vemos ahora la condición es que estemos en el mes de Enero (“Ene”), si es así lo sumamos. Al final, con END, indicamos la salida. Podemos reducir la expresión de la suma, con el operador “+=”, quedando así:
1 | awk '$1=="Ene" { sum += $5 }; END {print "Hemos ganado " sum "€ en total con todos los meses de Enero"}' inventario.txt |
Esto, evidentemente produce la misma salida:
Variables Especiales o built-in
Existen una serie de variables “especiales” o como las queramos llamar, que suponen una tremenda ayuda a la hora de usar AWK. En realidad ya hemos utilizado una en el anterior post, “FNR”, que indicaba el número de línea actual en el que estamos. Pero hay más.
FS (Field Separator)
Esta variable permite decir que vamos a usar como separación, por defecto su valor es ” “, como podemos ver en los archivos que hemos procesado hasta ahora, los campos estaban separados por un espacio. Pero este valor puede cambiar, simplemente haciendo una asignación. Imaginemos que tenemos el siguiente archivo:
1 2 3 4 | $ cat contactos.txt Carlos García,Calle Nueva N 27,Barcelona, 08124,carlos@gmail.com Rocío López,Calle Vieja N 2,Madrid,28562,rocio@google.com Patricia Díaz,Calle Secreta N 11,Cádiz,72723,patricia@hotmail.com |
En él se puede observar que hay datos de contactos, separados por comas. Y como ya sabemos el espacio es el separador que nos vale.
Imaginemos que vamos a hacer una lista de correo, y queremos extraer los correos de nuestros contactos. Se puede plantear la opción de modificar el archivo y cambiar las comas por espacios, pero lo cierto es que ya hay espacios, y si luego vamos a referirnos a la dirección, en $2, solo obtendríamos “Calle”, puesto que hay más espacios dentro de ese campo.
Así que la opción pasa por asignar el valor de “,” a la variable FS. Esto es algo que tenemos que hacer antes que nada, y para ello tenemos “BEGIN”. La instrucción quedaría así:
1 | awk 'BEGIN { FS = "," } ; { print $5 }' ./contactos.txt |
Nótese que aquí $5 se refiere al 5º campo entre comas que tenemos por línea, así, la salida sería:
Aquí hay un detalle, si en una línea tenemos 2 espacios seguidos, sería como tener “,,” es decir, una columna vacía, en cambio, si hay 2 espacios AWK no lo interpreta como un campo vacío entre medias, y se lo salta. Esto es por el lenguaje y para poder emular el mismo comportamiento necesitamos expresiones regulares, algo que veremos más adelante con detenimiento.
NF (Number of Fields)
Esta variable contiene el número de campos en la línea actual. Aprovechando el archivo anterior, para saber con cuántos campos estamos tratando:
1 | awk 'BEGIN { FS = "," } ; { print NF }' ./contactos.txt |
Y su salida será:
Es decir, tenemos 5 campos por línea, en todas las líneas. Se puede aprovechar el contenido de esta variable para referirse al último campo. Puesto que, de la misma forma que:
5 = 5
$5 es el contenido del campo 5
Con NF, pasa lo mismo:
NF = 5
$NF = contenido del campo 5.
Así que aprovechando esto, tendremos:
1 | awk 'BEGIN { FS = "," } ; { print $NF }' ./contactos.txt |
Y la salida sería exactamente la misma que si hubiéramos puesto $5.
Así que usando NF, no nos tenemos que preocupar de contar los campos, mientras que tengamos claro que lo que queremos obtener está en el último campo de cada línea.
NR (Number of Record)
NR contiene el número de registro en el que estamos, entendiendo registro como línea (aunque podría ser otra cosa, pero lo veremos más adelante).
Es importante que sepamos bien como es el archivo con el que vamos a trabajar, aquí estamos planteando ejemplos de archivos de pocas líneas pero con un archivo de 1000 o 2000 líneas, como puede pasar perfectamente en la realidad, no podemos ver a mano si todos los campos están bien y todo es uniforme.
Así si por ejemplo queremos ver que todas las líneas(registros) de un archivo tienen el mismo número de campos. Lo primero que debemos saber es como va a ser el informe que vamos a procesar, si le hemos pedido a los administrativos que hagan 3 columnas por línea, lo último que van a hacer va a ser eso, así que veamos como mirar si está mal.
El planteamiento es: si esperamos 3 y uno tiene algo distinto, lo decimos.
El archivo es este
1 2 3 4 5 | $ cat informe.txt Campo1 Campo2 Campo3 Campo1 Campo2 Campo3 Campo4 Campo1 Campo2 Campo3 Campo1 Campo2 Campo3 |
Como se puede ver, esperábamos tres campos y en la línea 2, hay 4. Nuestra instrucción quedaría:
1 2 3 4 | awk 'BEGIN { n = 3 }; { if (n != NF) {print "Error en la linea " NR ", tiene " NF " campos."} }' ./informe.txt |
Y su salida:
Si nos fijamos, ahora no nos interesa el contenido del registro número NF($NF), sino el valor en sí, es decir, el número de campos por línea, por ello usamos NF simplemente, que contendrá 3, 4, 3 y 3, según avance la ejecución. Además, NR nos aporta más información sobre el error, dicíendonos en que línea hay más campos de los esperados.
Dos cosas más a destacar es el uso del if y que existen varias líneas, en el programa. Respecto al uso del if, es como un if en el resto de lenguajes imperativos, con la estructura:
1 2 3 4 | if (condicion)
{accion}
else
{accion2} |
Y que el programa tenga varias líneas no es problema para el shell. Se interpreta como si estuviéramos introduciendo todo en una línea. Recordemos que las comillas simples al principio y al final del programa awk, no pertenecen propiamente al lenguaje, es una forma que tenemos para decirle al shell “lo que está entre las comillas simples no lo interpretes para ti, solo pásaselo a AWK”.
Conclusión
Hasta aquí llega esta parte, en la próxima veremos que una línea no tiene que ser un registro necesariamente, y profundizaremos más en AWK, con el uso de otras variables especiales y más funcionalidades que podamos darle a este lenguaje.
Fotografía de Maschinenraum (Flickr)

Sin comentarios
Trackbacks