AWK es ejemplo de un lenguaje de programación que usa ampliamente el tipo de datos de listas asociativas (es decir, listas indexadas por cadenas clave), y expresiones regulares. El poder, brevedad y limitaciones de los programas de AWK y los guiones de sed inspiraron a Larry Wall a escribir Perl. Debido a su densa notación, todos estos lenguajes son frecuentemente usados para escribir programas de una línea.
AWK fue una de las primeras herramientas en aparecer en Unix (en la versión 3) y ganó popularidad como una manera de añadir funcionalidad a las tuberías de Unix. La implementación de alguna versión del lenguaje AWK es estándar en casi todo sistema operativo tipo unix moderno. AWK es mencionado en las Single UNIX Specification (especificaciones básicas de unix) como una de las utilidades necesarias de todo sistema operativo Unix. Se pueden instalar implementaciones de AWK en casi todos los demás sistemas operativos.
En esta entrega vamos a explicar un uso básico y sencillo del comando AWK, que nos permitirá añadir funcionalidades muy interesantes al trabajo con tuberías en Unix y a la gestión de impresión de texto con el comando print.
Vamos a empezar con lo más simple, utilizar awk para imprimir por pantalla el contenido de un fichero de texto, tomamos como ejemplo el fichero /etc/hosts
awk '{ print }' /etc/hosts
Sacará por pantalla el contenido del fichero:
127.0.0.1 localhost 93.93.112.55 rm-rf.es www.rm-rf.es # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters ff02::3 ip6-allhosts
Entrando en materia, pongamonos en la situación de que necesitamos imprimir solamente la 1ª columna del fichero de texto. Awk toma por defecto como separador el espacio (o tabulación), así que para hacerlo haremos lo siguiente:
$ awk '{ print $1 }' /etc/hosts 127.0.0.1 93.93.112.55 # ::1 fe00::0 ff00::0 ff02::1 ff02::2 ff02::3
Hay que tener en cuenta que los valores especificados con el print($0,$1,$2…) representan cada una de las columnas en las que dividimos una línea. En caso de especificar $0, imprimiremos la línea completa:
$ awk '{ print $0 }' /etc/hosts 127.0.0.1 localhost 93.93.112.55 rm-rf.es www.rm-rf.es # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters ff02::3 ip6-allhosts
Ya que hablamos del separador, podemos especificarlo añadiendolo con -F «». Un ejemplo, queremos mostrar solamente la 1ª columna, pero tomando como separador el signo «:»
$ awk -F ":" '{ print $1 }' /etc/hosts fe00 ff00 ff02 ff02 ff02
Y si quisieramos la 3ª columna:
$ awk -F ":" '{ print $3 }' /etc/hosts 1 ip6-localhost ip6-loopback 0 ip6-localnet 0 ip6-mcastprefix 1 ip6-allnodes 2 ip6-allrouters 3 ip6-allhosts
Por supuesto podemos imprimir por pantalla varias columnas, añadir texto, etc. Ejemplo (he filtrado ciertas líneas con «grep» para que el resultado sea más limpio):
$ awk '{ print "La IP:\t" $1 " hace referencia al dominio " $2 }' /etc/hosts | grep -v "::" | grep -v "#" La IP: 127.0.0.1 hace referencia al dominio localhost La IP: 93.93.112.55 hace referencia al dominio rm-rf.es
Awk permite el uso de expresiones regulares para ejecutar selectivamente los bloques de código que concuerdan con la expresión regular. así como condicionales, variables numéricas y strings, operadores, etc. Para profundizar en ello revisar las fuentes de artículo al final del mismo.
Pasando a algún ejemplo práctico útil para la administración de sistemas:
ls *php | awk '{print "mv "$0" /home/alex/temporal/"$0".bak"}' | bash
En este ejemplo, lo primero que hacemos es listas todos los fichero php de una determinada carpeta (ls *php), posteriormente construimos con awk, el comando que queremos ejecutar para cada línea del fichero, en este caso,mover cada uno de los ficheros que sacamos con ls a una nueva ruta y renombrado a .bak:
mv index.php /home/alex/temporal/index.php.bak
mv test.php /home/alex/temporal/test.php.bak
mv prueba.php /home/alex/temporal/prueba.php.bak
Lo que hacemos es simplemente imprimir por pantalla dichas líneas. Como lo que queremos es ejecutarlas, con un pipe «|» hacemos que se ejecute con la orden «bash».
Así, con una simple línea hemos renombrado a «.bak» todos los ficheros de una carpeta y una determinada extensión y movido a otra ubicación.
Unos cuantos ejemplos más:
# Imprimir dos campos en orden inverso awk '{ print $2, $1 }' fichero
# Imprimir líneas con más de 72 caracteres awk 'length > 72' fichero
# Imprimir la longitud de la string de la segunda columna del fichero awk '{print length($2)}' fichero
# Añadir una columna e imprimir la suma y media: { s += $1 } END { print "sum is", s, " average is", s/NR }
# Imprimir registros en orden inverso awk '{ for (i = NF; i > 0; --i) print $i }' file
# Imprimir la última línea {line = $0} END {print line}
Muchos más ejemplos.
Tened en cuenta que muchos de estos ejemplos, al ser un poco más complejos, deben ser insertados en un fichero script con extensión .awk para ser ejecutados, utilizando la opción -f:
awk -f script.awk fichero
Referencias:
– IBM Awk
– The GNU Awk User’s Guide
– Hartigan/Computer/Awk