Iniciación a AWK

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.

Wikipedia

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

3 comentarios en “Iniciación a AWK

  1. Excelente articulo, ¿como sería para combinar columnas de dos archivos distintos en un nuevo archivo separado por «:» con la siguiente sintaxis?

    awk ‘{ print $2, $1 }’ fichero
    por ejemplo, un fichero tiene nombre:dni y otro nombre:direccion, ¿como creariamos un fichero con contenido nombre:dni:dirección?

  2. Buenas…
    Estoy teniendo un problema, resulta que tengo registros muy grandes y se desbordan

    Tengo lineas en el fichero que me ocupan mas de ‘10239’
    Al parecer el AWK, cuando un registro pasa de ‘10239’ caracteres, lo trata como la siguiente fila.

    ¿ Sabeis si existe algun valor para modificar esto y hacerlo mayor ?
    Gracias
    Un saludo

Comments are closed.