2. Empezando con awk

La función básica de awk es buscar líneas en ficheros (u otras unidades de texto) que contienen ciertos patrones. Cuando en una línea se encuentra un patrón, awk realiza las acciones especificadas para dicho patrón sobre dicha línea. Awk sigue realizando el procesamiento de las líneas de entrada de esta forma hasta que se llega al final del fichero.

Cuando ejecutas awk, especificas un programa awk que le dice a awk que tiene que hacer. El programa consiste en una serie de reglas (podría también contener definiciones de funciones, pero esa es una característica avanzada, así que ignorémosla por ahora. Ver la sección 11. Funciones definidas por el Usuario). Cada regla especifica un patrón a buscar, y una acción a realizar cuando se encuentre dicho patrón en el registro de entrada.

Sintácticamente, una regla consiste en un patrón seguido por una acción. La acción se encierra entre llaves para separarlas de los patrones. Las reglas están separadas por caracteres newline. Por lo tanto, un programa awk presentaría la siguiente forma:

patrón { acción }

patrón { acción }

...

Un ejemplo muy simple

El siguiente comando ejecuta un programa awk simple que busca la cadena de caracteres ‘foo’ en el fichero de entrada ‘Lista–BBS’ y si la encuentra la imprime. (Las cadenas de caracteres son normalmente llamadas cadenas. )

awk '/foo/ { print $0 }' Lista–BBS

Cuando se encuentran líneas que contengan ‘foo’, éstas son impresas, ya que ‘print $0’ hace que se imprima la línea actual.

Habrás advertido las barras, ‘/’, alrededor de la cadena ‘foo’ en el programa awk. Las barras indican que ‘foo’ es un patrón para búsqueda. Este tipo de patrón se llama expresión regular, y es cubierta con más detalle posteriormente (Ver la sección Expresiones Regulares como Patrones). Existen comillas simples alrededor del programa awk para que el shell no interprete el programa como caracteres especiales de la shell.

Aquí se muestra lo que este programa imprime:

fooey        555–1234     2400/1200/300     B
foot         555–6699     1200/300          B
macfoo       555–6480     1200/300          A
sabafoo      555–2127     1200/300          C

En una regla awk, o el patrón o la acción puede ser omitida, pero no ambos. Si el patrón se omite, entonces la acción se realiza para cada línea de entrada. Si se omite la acción, la acción por defecto es imprimir todas las líneas que concuerden con el patrón.

Por lo que, podríamos omitir la acción (la sentencia print y las llaves) en el ejemplo anterior, y el resultado sería el mismo: todas las líneas que concuerden con el patrón ‘foo’ serán impresas. Por comparación, la omisión de la sentencia print pero manteniendo las llaves produce una acción vacía que no realiza nada; por lo que no se imprimirá ninguna línea.

Un ejemplo con dos reglas

La utilidad awk lee los ficheros de entrada línea a línea. Para cada línea, awk comprueba todos los patrones de todas las reglas. Si concuerdan varios patrones entonces se ejecutan las distintas acciones de cada patrón, en el orden en que aparecen en el programa awk. Si no concuerda ningún patrón, entonces no se ejecuta ninguna acción.

Después de ejecutar las acciones de las reglas que concuerden con la línea (quizás no concuerde ninguna), awk lee la siguiente línea (aunque existen excepciones, Ver la sección La Sentencia next). Esto continúa hasta que se alcanza el final de fichero.

Por ejemplo, el programa awk:

/12/  { print $0 }
/21/  { print $0 }

contiene dos reglas. La primera regla tiene la cadena ‘12’ como patrón y realiza la acción ‘print $0’. La segunda regla tiene la cadena ‘21’ como patrón y también realiza la acción ‘print $0’. La accion/es de cada regla se encierra entre un par de llaves.

Este programa imprime cada línea que contiene o la cadena ‘12’ o la cadena ‘21’. Si una línea contiene ambas cadenas, ésta línea es impresa dos veces, una vez por cada regla.

Si ejecutamos este programa sobre nuestros dos ficheros de datos de ejemplo, ‘Lista–BBS’ y ‘inventario–enviado’ (Ver el apéndice A – Ficheros de Datos para los Ejemplos), como se muestra aquí:

awk '/12/ { print $0 }

     /21/ { print $0 }' Lista–BBS inventario–enviado

obtenemos la siguiente salida:

aardvark     555–5553     1200/300          B

alpo-net     555–3412     2400/1200/300     A

barfly       555–7685     1200/300          A

bites        555–1675     2400/1200/300     A

core         555–2912     1200/300          C

fooey        555–1234     2400/1200/300     B

foot         555–6699     1200/300          B

macfoo       555–6480     1200/300          A

sdace        555–3430     2400/1200/300     A

sabafoo      555–2127     1200/300          C

sabafoo      555–2127     1200/300          C

Jan  21  36  64 620

Apr  21  70  74 514

Dese cuenta de que la línea contenida en ‘Lista–BBS’ que empieza por ‘sabafoo’ fue impresa dos veces, una vez para cada regla.

Un ejemplo más complejo

Aquí tienes un ejemplo para darte una idea de lo que puede hacer un programa awk típico. Este programa muestra como awk puede ser usado para sumarizar, seleccionar y relocalizar la salida de otra utilidad. En el ejemplo se usan características que no han sido todavía explicadas, así que no te preocupes si no entiendes todos los detalles.

ls –l ¦¦ awk '$5 == "Nov" { sum += $4 }

             END { print sum }'

Este comando imprime el número total de bytes de todos los ficheros del directorio actual que fueron modificados por última vez en Noviembre (de cualquier año). (En el C Shell necesitarás teclear un punto y coma y después una barra invertida al final de la primera línea; en el Shell de Bourne o el Shell Bourne–Again puedes teclear el ejemplo tal y como se muestra).

La parte de este ejemplo `ls –l` es un comando que te da un listado completo de todos los ficheros de un directorio, incluyendo el tamaño del fichero y la fecha. La salida es algo como lo siguiente:

-rw-r--r--  1 close        1933 Nov  7 13:05 Makefile

-rw-r--r--  1 close       10809 Nov  7 13:03 gawk.h

-rw-r--r--  1 close         983 Apr 13 12:14 gawk.tab.h

-rw-r--r--  1 close       31869 Jun 15 12:20 gawk.y

-rw-r--r--  1 close       22414 Nov  7 13:03 gawk1.c

-rw-r--r--  1 close       37455 Nov  7 13:03 gawk2.c

-rw-r--r--  1 close       27511 Dec  9 13:07 gawk3.c

-rw-r--r--  1 close        7989 Nov  7 13:03 gawk4.c

El primer campo presenta los permisos de lectura–escritura, el segundo campo contiene los enlaces al fichero, y el tercer campo identifica al propietario del fichero. El cuarto campo contiene el tamaño del fichero en bytes. Los campos quinto, sexto y séptimo contienen el mes, el día y la hora, respectivamente, en la cual el fichero fue modificado por última vez. Finalmente, el octavo campo contiene el nombre del fichero.

La expresión $5==”Nov” de tu programa awk es una expresión que chequea si el quinto campo de la salida generada por ‘ls –l’ es igual a ‘Nov’. Cada vez que una línea presenta la cadena ‘Nov’ en su quinto campo, se realiza la acción ‘{ sum += $4 }’. Esto añade el cuarto campo (el tamaño del fichero) a la variable sum. Como resultado, cuando awk ha finalizado la lectura de líneas de entrada, sum es la suma de los tamaños de los ficheros cuyas líneas contuvieron el patrón.

Después de que la última línea de la salida generada por ls haya sido procesada, se ejecuta la regla END, y el valor de la variable SUM se imprime. En este ejemplo, el valor de sum sería 80600.

Estas técnicas de awk más avanzadas serán cubiertas en secciones posteriores. (Ver la sección 7. Acciones: Overview). Antes de enseñarte la programación de awk más avanzada, debes saber como awk interpreta tu entrada y visualiza tu salida. Manipulando campos y usando sentencia print, puedes producir algunos informes muy útiles y de apariencia espectacular.

Cómo ejecutar los programas awk

Hay varias formas de ejecutar un programa awk. Si el programa es corto, es más fácil incluir el programa en el mismo comando que ejecuta awk, de la siguiente forma:

awk ’program’ input–file1 input–file2 ...

donde program consiste en una serie de patrones y acciones, según se describió anteriormente.

Cuando el programa es largo, probablemente preferirías poner el programa en un fichero y ejecutarlo con un comando de la siguiente forma:

awk –f program–file input–file1 input–file2 ...

Programas de ejecución rápida (One–shot Throw–away)

Una vez que estás familiarizado con awk, escribirás con frecuencia programas simples sobre la marcha para solucionar algo puntual. Puedes escribir el programa como el primer argumento del comando awk, de la siguiente forma:

awk ’programa’ input–file1 input–file2 ...

donde programa consiste en una serie de patrones y acciones, como se describieron anteriormente.

Este formato de comando le dice al shell que ejecute awk y use programa para procesar los registros en el fichero(s) de entrada. Aparecen comillas simples alrededor del programa de forma que el shell no interprete ningún carácter awk como carácter especial del shell. Esto hace que el shell trate programa por completo como un único argumento de awk. Por lo tanto permite que programa tenga una extensión de varias líneas.

Este formato es también útil para la ejecución de programas pequeños y medios desde shell scripts, porque evita la necesidad de un fichero separado para el programa awk. Un shell script empotrado en la línea de comando es más fiable ya que no hay otros ficheros implicados para que se produzcan fallos.

Ejecutar awk sin Ficheros de Entrada

También puedes usar awk sin ficheros de entrada. Si tecleas la siguiente línea de comando:

awk ’programa’

entonces awk aplica el programa a la entrada estándar, que en la mayoría de los casos es todo lo que tecleas en el terminal.

Esto continuará hasta que indiques el final de fichero mediante la combinación de teclas Control–d.

Por ejemplo, si tu ejecutas el siguiente comando:

awk '/th/'

cualquier cosa que teclees a continuación será cogido como datos para tu programa awk. Si continúas y tecleas los siguientes datos:

Kathy

Ben

Tom

Beth

Seth

Karen

Thomas

Control–d

Entonces awk imprime la siguiente salida:

Kathy

Beth

Seth

como las líneas que contienen el patrón especificado ‘th’. Dese cuenta de que no reconoce “Thomas” como línea que cumpla el patrón. El lenguaje awk hace distinciones entre mayúsculas y minúsculas, y busca la concordancia con el patrón exacta. (Sin embargo, puedes evitar esto con la variable IGNORECASE. Ver la sección Sensibilidad a Mayúsculas en el Matching.)

Ejecución de Programas Grandes

Algunas veces, tus programas awk pueden ser muy grandes. En este caso, es más conveniente poner el programa en un fichero separado. Para decirle a awk que use ese fichero como programa, se teclea:

awk –f fichero–fuente input–file1 input–file2 ...

El ‘–f’ le dice a la utilidad awk que obtenga el programa awk del fichero fichero–fuente. Cualquier nombre de fichero puede ser usado como fichero–fuente. Por ejemplo, podrías poner el programa:

/th/

en el fichero ‘th–prog’. Entonces este comando:

awk –f th–prog

hace la misma cosa que este otro:

awk ´/th/´

lo cual fue explicado anteriormente (ver sección Ejecutar awk sin Ficheros de Entrada). Dese cuenta que no necesitas normalmente comillas simples alrededor del nombre del fichero que especificas con ‘–f’, porque la mayoría de los nombres de ficheros no contienen ninguno de los caracteres especiales de la shell.

Si quieres identificar tus ficheros con programas awk como tales, puedes añadir la extensión ‘.awk’ a los nombres de ficheros. Esto no afecta a la ejecución de un programa awk, pero hace que el nombre del fichero sea más legible y fácil de localizar.

Programas awk Ejecutables

Una vez que hayas aprendido awk, podrías querer escribir scripts de awk contenidos dentro de un Shell Script ejecutable de Unix, usando el mecanismo de script ‘#!’. Puedes hacer esto en sistemas Unix BSD y (algún día) en GNU.

Por ejemplo, podrías crear un fichero texto llamado ‘hola’ que tuviese lo siguiente (donde ‘BEGIN’ es una característica todavía no vista):

#! /bin/awk –f

# un programa awk de ejemplo

BEGIN { print "Hola Mundo" }

Después de hacer este fichero ejecutable (con el comando chmod), puedes teclear simplemente:

hola

desde el shell, y el sistema ejecutará el awk del mismo modo que si hubieses tecleado:

awk –f hello

Los scripts de awk auto contenidos (introducidos dentro de un Shell Script ejecutable de Unix) son útiles cuando quieres escribir un programa que puedan invocar los usuarios sin que sepan que el programa está escrito en awk.

Si tu sistema no soporta el mecanismo ‘#!’, puedes obtener un efecto similar usando un shell script regular. Sería algo del estilo a esto:

: Los dos puntos te aseguran de que este script se ejecuta con el shell de Bourne.

awk 'program' "$@"

Usando esta técnica, es vital encerrar el program entre comillas simples para proteger que el programa sea interpretado por el shell.

El ‘"$@"’ hace que el shell se salte todos los argumentos de la línea de comando al programa awk, sin interpretación. La primera línea, que comienza con un colon, se usa de forma que este shell script funcionará incluso si es invocado por un usuario que use la C–Shell.

Comentarios en Programas awk

Un comentario es un texto que es incluido en el programa para que dicho programa sea más entendible por los lectores del mismo; no siendo los comentarios realmente parte del programa. Lo comentarios pueden explicar que es lo que hace el programa, y cómo lo hace. Prácticamente todos los lenguajes de programación tiene alguna forma para introducir comentarios, ya que hay muchos programas realmente difíciles de entender sin su ayuda extra en forma de comentarios.

En el lenguaje awk, un comentario comienza con el signo almohadilla, ‘#’, y dicho comentario continúa hasta el final de la línea. El Lenguaje awk ignora el resto de una línea desde el momento que encuentra un carácter ‘#’. Por ejemplo, podríamos haber puesto lo siguiente en el programa ‘th–prog’.

# Este programa encuentra registros que contengan el patrón ‘th’. De esta forma

# es como continuas el comentario en una línea adicional.

/th/

Sentencias frente a Líneas en awk

Bastante a menudo, cada línea en un programa awk es una sentencia separada o regla separada, como esta:

awk ’/12/  { print $0 }

     /21/  { print $0 }’ Lista–BBS inventario–enviado

Pero algunas veces una sentencia puede ocupar más de una línea, y una línea puede contener varias sentencias. Puedes partir una sentencias en múltiples líneas insertando un carácter newline (salto de línea) detrás de alguno de los siguientes caracteres:

,    {    ?    :    ¦¦    &&    do    else

Un carácter newline el cualquier otro punto es considerado como el final de una sentencia.

Si te gustaría partir una única sentencia en dos líneas en un determinado punto en el cual un carácter newline terminaría dicha sentencia, puedes continuarla finalizando la primera línea con un carácter de barra invertida, ‘\’. Esto esta permitido absolutamente en cualquier sitio de la sentencia, incluso en mitad de una cadena o expresión regular. Por ejemplo:

awk '/Este programa es demasiado grande, así que continua \

en la línea siguiente / { print $1 }'

Nosotros no hemos utilizado, por norma, el uso de la continuación de una línea mediante la barra invertida en los programas de ejemplo de este manual. Ya que no hay límite en la longitud de una línea, nunca es estrictamente necesario particionar ésta; es simplemente por cuestión de estética. La continuación mediante barra invertida es muy útil cuando tu programa awk está en un fichero fuente independiente, en lugar de ser tecleado en la línea de comandos.

Precaución: la continuación de línea usando la barra invertida no funciona tal y como se describe aquí bajo la C Shell. La continuación con barra invertida funciona en tus ficheros con programas awk, y también en los programas que se lanzan escribiéndolos directamente en la línea de comandos teniendo en cuenta que estés usando el Bourne shell o Bourne–again Shell. Pero el C shell usado en Unix Berkeley se comporta de forma diferente! Bajo este Shell, debes usar dos barras invertidas en una fila, seguido por un carácter newline.

Cuando las sentencias awk dentro de una regla son cortas, podrías desear poner más de una en una misma línea. Se puede hacer esto separando las sentencias mediante puntos y coma ‘;’. Esto se aplica también a las mismas reglas. Por lo que, el programa de más arriba se podría haber escrito:

/12/ { print $0 } ; /21/ { print $0 }

Nota: el requerimiento de que las reglas en la misma línea deben ser separadas por puntos y comas es un cambio reciente en el Lenguaje awk; fue hecho por consistencia con el tratamiento de las sentencias dentro de una acción.

Cuando usar awk

Te estarás preguntando: ¿qué uso le puedo yo dar a todo esto? Utilizando programas de utilidades adicionales, patrones más avanzados, separadores de campo, sentencias aritméticas, y otros criterios de selección, puedes producir una salida mucho más compleja.

El lenguaje awk es muy útil para producir informes a partir de grandes cantidades de datos raw, tales como información de sumarización a partir de la salida de otros programas de utilidades tales como ls. (Ver la sección Un ejemplo más complejo).

Los programas escritos con awk son a menudo mucho más pequeños de lo que serían en otros lenguajes. Esto hace que los programas awk sean más fáciles de componer y usar. A menudo los programas awk pueden ser compuestos rápidamente en tu terminal, usados una y múltiples veces. Debido a que los programas de awk son interpretados, puedes evitar el ciclo de desarrollo de software normal.

Han sido escritos en awk programas complejos, incluido un completo ensamblador para microprocesadores de 8 bits (Ver la sección 18. Glosario, para más información) y un ensamblador microcodificado para una computadora Prolog de propósito especial. Sin embargo, las posibilidades de awk van más allá de tales complejidades.

Si te encuentras a ti mismo escribiendo scripts de awk de más de, digamos, unos pocos de cientos de líneas, podrías considerar usar un lenguaje de programación diferente. Emacs Lisp es una buena elección si necesitas cadenas sofisticadas o capacidades de búsqueda de patrones. El shell también está bien para búsqueda de patrones y cadenas; además, te permite el potente uso de utilidades del sistema. Lenguajes más convencionales, tales como C, C++, y Lisp, te ofrecen mejores facilidades para la programación de sistema y para manejar la complejidad de grandes programas.

   
Índice
Manual