Las esquinas oscuras de AWK

AWK es un lenguaje de Escaneo y procesamiento de patrones.

Dark Corners (poorly documented or not documented at all)

Brian Wilson Kernighan“Dark corners are basically fractal — no matter how much you illuminate, there’s always a smaller but darker one.” –Brian Kernighan.

Gawk es una implementación mejorada de parte desde el proyecto GNU para el lenguaje de programación AWK. La línea de comando consite de opciones:

  • Propias de Gawk.
  • El programa de texto Awk (si no se especifíca vía -f o –file).
  • Los valores disponibles mediante las variables predefinidas ARGC y ARGV.

Pgawk es la versión “profiling” de gawk. Es identíco en todo aspecto a Awk, excepto que los programas corren más despacio, debido a que producen de manera automática un perfil (awkprof.out) Por favor consulte la opción –profile.

Dgawk es un “debugger“, en vez de correr el programa de manera directa, más bien carga el código fuente del programa y luego pregunta por comandos necesarios para hacer debugging. A diferencia de Gawk en Pgawk, Dgawk sólo procesa el código fuente suministrado con la opción -f. El debugger se encuentra documentado en el recurso “GAWK: Effective AWK Programming“.

La función básica de awk permite buscar líneas en archivos (unidades de texto) que contienen ciertos patrones. Cuando una línea coincide con uno de los patrones, awk realiza las acciones especificadas en esa línea. awk se mantiene procesando líneas de entrada hasta llegar al final de los archivos de entrada. Los programas en awk son diferentes a los de otros lenguajes.

Formato de opciones

Las opciones de Gawk pueden ser cualquiera de las dos, el estilo tradicional POSIX conformado por opciones de una letra o el estilo GNU de opciones largas.

Las opciones POSIX empiezan con un sólo guión “-” y las opciones largas GNU empiezan con dos. Los opciones largas se encuentran disponibles para características específicas de GNU y para características mandated de POSIX.

Empecemos con awk

Como los programas awk son basados en datos, eso es todo, se describen los datos con los que deseamos trabajar y se dice que hacer cuando se encuentran. La mayoría lenguajes de programción son procesales, hay que describir en detalle cada paso que el programa toma. Cuando se trabaja con lenguas procedimentales, por lo general es más difícil describir con claridad los datos vamos a procesar con el programa. Por esta razón, con frecuencia, los programas awk son fáciles de escribir y de leer.

Cuando ejecutamos un programa awk, le dicimos a awk qué es lo que dede hacer. El programa consiste en una serie de reglas, donde cada regla especifica un patrón a buscar y una acción a realizar cuando se encuentra el patrón. Sintácticamente, una regla consiste de un patrón seguido por una acción.

La accións encuentra entre llaves, se separa del patrón. De manera usual, las reglas se separan por líneas. Por lo tanto, un programa awk se puede ve así:

pattern {action}
pattern {action}

¿Cómo ejecutar programas AWK?

Hay varias formas de ejecutar un programa awk. Si el programa es corto, más fácil correrlo desde la línea de comando que corre awk, por ejemplo:

awk SCRIPT archivo-entrada-1 archivo-entrada-2 ...

Cuando el programa es largo, es más conveniente realizar un script en un archivo, para que pueda ser corrido como en el siguiente ejemplo:

awk -f SCRIPT archivo-entrada-1 archivo-entrada-2 ...

Trataremos ambos mecanismos, junto con algunas variaciones en cada uno.

Una mirada a Progamas AWK

Una vez que se esté familiarizado con awk, es más simple escribir programas que utilicen awk. De esta manera se puede escribir programas como el primer argumento de un programa awk, por ejemplo:

awk SCRIPT archivo-entrada-1 archivo-entrada-2 ...

donde el programa es compuesto por una serie de patrones y acciones.

Este formato de comandos realiza: Ordenes al shell o intérprete de comandos, para iniciar awk y utilizar el programa para procesar los registros de los archivos de entrada. Se usan las comillas simples alrededor del programa, de esta manera el shell no interpretará los caracteres awk como caracteres especiales del shell.

Las comillas simples hacen que el shell trate todo el programa como un solo argumento para awk, lo que permite que el programa sea de más de una línea. Este formato es muy útil para correr programas awk que sean cortos o mediano a partir de shell scripts, esto evita la necesidad de usar un archivo separado para el programa awk.

Un script de shell autónomo es más confiable, porque no hay otros archivos que puedan perderse. Parece muy simple, más adelante, se presentan varios programas cortos independientes (self-contained programs).

Corriendo awk sin archivos de entrada

Awk también se puede correr sin especificar archivos de entrada, es decir, lo que sea que se escriba en la terminal o línea de comandos:

awk 'programita'

awk aplica el programa a la entrada estándar, que por lo general significa: lo que sea que se escriba en la terminal. Esto continúa hasta se indica el fin de un archivo (Ctrl-D). (En otros sistemas operativos, el carácter de fin de archivo puede ser diferente. Por ejemplo, en OS/2, es Ctrl-z).

Por ejemplo, el siguiente programa imprime un trozo o pequeño consejo (de Douglas Adams “Guide to the Galaxy”), para evitar preocupaciones debido a las complejidades de la programación de computadoras (BEGIN es una característica que no hemos empezado a discutir):

awk "BEGIN { print \"'Tranquilidad'\" }"

Este programa no lee archivo de entrada. El uso de ‘\’ antes de cada una de las comillas dobles interiores es necesario debido a las reglas de de comillasparticulares del shell, en este ejeplo, se mezclan las comillas simples y las comillas dobles también.

El siguiente programa awk emula la utilidad del comando cat, copia lo que sea que se escriba en el teclado hacia la salida estándar:

awk '{ print }'
Ahora es el momento para
todos los hombres buenos.

Presione CTRL-d para salir.

Corriendo programas largos

A veces los programas awk pueden ser muy largos. En este caso, es más conveniente poner el programa en un archivo separado. Con esto se le dice a awk que utilice un archivo para su programa, de esta manera se puede usar:

awk-f SCRIPT archivo-entrada-1 archivo-entrada-2 ...

La opción -f le dice a awk que obtenga el programa de un SCRIPT o archivo fuente. Cualquier nombre de archivo se puede utilizar para ste archivo fuente. Por ejemplo, se puede crear el siguiente trozo de programa:

vi tranquilidad.awk

Begin {print "Tranquilidad" }

Entonces utilice el siguiente:

awk -f tranquilidad.awk

en esencia hace lo mismo que:

awk "BEGIN { print \"Tranquilidad\" }"

De manera general no se necesita comillas simples alrededor del nombre de archivo que se especifica con la opción -f, ya que la mayoría de los nombres de archivo no contienen ninguno de los caracteres especiales del shell.

Pero en tranquilidad.awk, el programa awk no tiene comillas simples alrededor. Las comillas sólo son necesarios para los programas que se corren en la línea de comando awk. Si desea identificar claramente los archivos de programa awk como tales, se puede añadir la extensión .awk al nombre del archivo. Esto no afecta la ejecución del programa awk, además hace más fácil el “housekeeping“.

Programas awk ejecutables

Una vez que se aprende awk, es posible escribir scripts awk autónomos, a través del mecanismo “#!” para scripts. Por ejemplo, se puede modificar archivo tranquilidad.awk agregando al principio lo siguiente:

vi tranquilidad.awk

#! /usr/bin/awk -f

Begin {print "Tranquilidad" }

Además se cambian sus permisos:

cmmod +x tranquilidad.awk

Seguidamente puede correr desde la terminal con:

./tranquilidad.awk

Los scripts awk autónomos son de gran ayuda cuando se escribe un programa que los usuarios pueden invocar sin tener la idea de que el programa está escrito en awk.

Problemas de portabilidad con “#!”

Algunos sistemas limitan la longitud del nombre del intérprete a 32 caracteres. A menudo, esto se puede trabajar mediante el uso de un enlace simbólico. Después de la ruta de awk en la línea “#!”, no se debe colocar más de un argumento. Es importante saber que el valor de ARGV[0] puede variar dependiendo del sistema operativo.

Algunos sistemas colocan a awk ahí, otros colocan la ruta completa de awk (tal como /bin/awk o /usr/bin/awk), y otros colocan el nombre del propio script (tranquilidad.awk). Por favor con confíe en el valor de ARGV[0] para proveer el nombre del script.

Los comentarios en los programas awk

Un comentario es un texto que se incluye en un programa por el bien de los lectores humanos, no se una parte ejecutable del programa. Los comentarios pueden explicar lo que hace el programa y cómo funciona.

Casi todos los lenguajes de programación permiten comentarios, ya que los programas algunas veces pueden ser difíciles de entender sin ellos. En el lenguaje awk, un comentario comienza con el símbolo # (sharp sign) y continúa hasta el final de la línea.

El carácter # no tiene que ser el primer carácter de la línea. El lenguaje awk ignora el resto de la línea después de un símbolo #. Por ejemplo, se colocar el siguiente comentario en el script tranquilidad.awk:

vi tranquilidad.awk

#! /usr/bin/awk -f

# Este programa imprime un mensaje optimista.
# Permite que todas las personas no teman a las computadoras.

Begin {print "Tranquilidad" }

El propósito de un comentario sirve para ayudarnos y para ayudar a otras personas a entender el programa al leerlo en un momento posterior.

PRECAUCIÓN: Se puede encerrar programas cortos o medianos entre comillas simples, con el fin de mantener los scripts de shell autónomos. Al hacerlo, no se deben colocar, por ejemplo, una apóstrofe (es decir, una comilla simple) dentro de un comentario (o en cualquier otro lugar del programa). Si esto se hace entonces el shell interpreta la comilla simple como la que cierra todo el programa. Como resultado, por lo general el shell muestra un mensaje sobre comillas simples coincidentes, y si awk en realidad corre, es probable que imprima mensajes extraños acerca de errores de sintaxis. Por ejemplo:

awk '{ print "hello" } # let's be cute'

El shell entiende que los dos primeros encomillados coinciden , y que un nuevo encomillado empieza al final de la línea. Por lo tanto se queda esperando “prompt”, esperando por más entradas. En awk de Unix, por ejemplo:

awk '{ print "hello" } # let's be cute'
'

error--> awk: can't open file be
error-->  source line number 1

Colocar una barra invertida (backslash) antes de las comillas simples en “let’s” no va a funcionar, ya que las barra invertida no son especiales dentro de comillas simples.

Cuestiones de comillas en el Shell

Para programas awk cortos o medianos, es más conveniente entrar el programa en la línea de comandos awk. Esto se hace encerrando todo el programa entre comillas simples. Esto es cierto si está entrando en el programa de forma interactiva en el intérprete de comandos (shell prompt), o si se escribe como parte de un Script de Shell más grande:

awk-f 'programa en texto' archivo-entrada-1 archivo-entrada-2 ...

Cuando se trabaja con el shell sirve tener un conocimiento básico de las reglas de comillas del shell. Las siguientes reglas sólo aplican a Shells estilo Bourne compatibles con POSIX (tales como Bash, el GNU Bourne-Again Shell). Si se utiliza el C Shell, se está relativamente sólo.

Elementos entre comillas se pueden concatenar con otros elementos que no estén entre comillas, así como con otros elementos que sí lo estén. El shell convierte todo en un argumento para el comando. Precediendo a cualquier carácter con una barra invertida (‘\’) coloca entre comillas ese carácter. El shell elimina la barra invertida y pasa el carácter entre comillas en el comando.

Las comillas simples protegen todo lo que se encuentra entre la apertura y el cierre de comillas. El shell no hace ninguna interpretación del texto entre comillas, lo pasa textualmente al comando.

Es imposible insertar una comilla simple dentro de un texto que se encuentre de por sí entre comillas simples. Las comillas dobles protegen la mayoría de las cosas entre la apertura y el cierre de comillas. El shell al menos hace sustituciones de variable y de comandos dentro del texto entre comillas.

Shells diferentes pueden hacer cosas adicionales al procesar texto entre comillas dobles. Dado que ciertos carácteres dentro de las comillas dobles son procesados ​​por el shell, deben ser escapadas dentro del texto. Hay que notar que los caracteres ‘$‘, ‘`‘, ‘\‘, y ‘‘, deben ser precedidos por una barra invertida dentro de las comillas dobles texto si van a ser transmitidas literalmente al programa. Por ejemplo:

awk "BEGIN { print \"'Tranquilidad'\" }"

Hay que tomar en cuenta que la comilla simple no es especial dentro de comillas dobles. Cadenas vacías se remueven cuando se producen como parte de un argumento no nulo en la línea de comandos, mientras que los objetos no nulos explícitos se mantienen. Por ejemplo, para especificar que el separador de campo FS se podría configurar a la cadena null, utilice:

awk -F "" 'programita' ARCHIVOS

No utilice la siguiente:

awk -F"" 'programita' ARCHIVOS

En el segundo caso, awk tratará de usar el texto del programa como el valor de FS, y el primer nombre de archivo como el texto del programa. Esto resulta en errores de sintáxis, además de empeorar el comportamiento.

Trucos

Mezclar comillas simples y comillas dobles es difícil. Hay que recurrir a trucos, como el siguiente:

awk 'BEGIN { print "Aquí hay una linda comilla simple '"'"' " }'

Este programa consta de tres cadenas entre comillas que se encuentran concatenadas. La primera y la tercera son comillas simples, la segunda es de comillas dobles. Esto se puede “simplificar” en:

awk 'BEGIN { print "Aquí hay una linda comilla simple '\''" }'

Otra opción es utilizar comillas dobles:

awk "BEGIN { print \"Aquí hay una linda comilla simple '\" }"

Esta opción suele ser dolorosa, porque las comillas dobles, las barras invertidas, y los signos de dólar son muy comunes en los programas awk más avanzados. Una tercera opción es usar las secuencias de escape equivalentes en octal para los caracteres entre comillas simples y entre comillas dobles, de este modo:

awk 'BEGIN { print "Aquí hay una linda comilla simple \47" }'

awk 'BEGIN { print "Aquí hay una linda comilla doble \42" }'

Esto funciona muy bien, excepto que se debe comentar con claridad lo que significan los “escapes“. Una cuarta opción es utilizar la asignación de variable de la línea de comandos, algo así:

awk -v sq="'" 'BEGIN { print "Aquí hay una linda comilla simple "sq"" }'

Si realmente se necesita usar ambos el programa awk, es probable que es mejor moverse hacia un archivo aparte, donde el shell no sea parte de la pintura, aquí se puede decir lo que se quiera decir al respecto.

Entrecomillado dentro de los archivos en lote de MS Windows

Aunque estamos enfocados en los sistemas POSIX y el shell POSIX, el problema siguiente se plantea a menudo para muchos usuarios de MS Windows, por esta razón es que se decide abordar el tema.

Los “shells” en los sistemas Microsoft Windows utilizan el carácter de comillas dobles para entrecomillar, y hacen que sea difícil o imposible el hecho de incluir un carácter de comillas dobles escapado en un script en la línea de comandos.

El siguiente ejemplo muestra cómo imprimir líneas en un archivo que se encuentran entre comillas dobles:

gawk "{ print \"\042\" $0 \"\042\" }" ARCHIVO

Archivos de datos para los ejemplos

Muchos de los ejemplos de este paso a paso toman su entrada de dos archivos de datos de muestra. El primero es, “BBS-list”, representa una lista “bulletin board systems” con información sobre dichos sistemas. El segundo archivo de datos, llamado “inventory-shipped”, contiene información sobre los envíos mensuales. En los dos archivos, cada línea es considerada como un registro.

En el archivo de datos BBS-list, cada registro contiene el nombre de un “computer bulletin board”, su número de teléfono, la velocidad de transmisión de tarjeta, y un código para el número de horas que esté en funcionamiento. Una ‘A’ en la última columna significa que la tarjeta funciona las 24 horas del día. Una ‘B’ en la última columna significa que la tarjeta sólo funciona en la tarde y los fines de semana. Una ‘C’ significa que la tarjeta funciona sólo los fines de semana:

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
camelot      555-0542     300               C
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

El archivo de datos “inventory-shipped” representa la información sobre los envíos durante el año. Cada registro contiene el mes, el número de cajas verdes enviadas, el número de cajas de color rojo enviadas, el número de bolsas de color naranja enviadas, y el número de paquetes azul enviados. Hay 16 entradas, que abarcan los 12 meses del año pasado y los primeros cuatro meses del año en curso.

Jan  13  25  15 115
Feb  15  32  24 226
Mar  15  24  34 228
Apr  31  52  63 420
May  16  34  29 208
Jun  31  42  75 492
Jul  24  34  67 436
Aug  15  34  47 316
Sep  13  55  37 277
Oct  29  54  68 525
Nov  20  87  82 577
Dec  17  35  61 401

Jan  21  36  64 620
Feb  26  58  80 652
Mar  24  75  70 495
Apr  21  70  74 514

Algunos ejemplos sencillos

El siguiente comando corre un simple programa awk que busca la cadena de carácteres “foo” en el archivo “BBS-list” de entrada para la cadena de caracteres ‘foo’ (un grupo de carácteres se suele llamar una cadena):

Cuando awk encuentra las líneas que contienen “foo”, las imprime mediante “print $0” que significa imprimir la línea actual (usar sólo “print” significa lo mismo).

awk '/foo/ { print $0 }' BBS-list

Se puede notar que la palabra “foo” se encuentra rodeada o entre dos barras (“/”). Las barras indican que “foo” es el patrón que se desea buscar. Este tipo de patrón se conoce como una expresión regular (ver Regexp). El patrón permite encontrar coincidencias con partes de palabras. Como hay comillas simples alrededor del programa awk, el shell no interprete nada de esto como caracteres especiales de shell.

Esto es lo que imprime este programa:

awk '/foo/ { print $0 }' BBS-list
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 de awk, ya sea el patrón o la acción se pueden omitir, pero no ambos. Si se omite el patrón, entonces se realiza la acción para cada línea de entrada. Si se omite la acción, la acción predeterminada es imprimir todas las líneas que coincidan con el patrón.

Por lo tanto, en el ejemplo anterior, podemos dejar de lado la acción (la sentencia print y las llaves) y el resultado sería el mismo: awk imprime todas las líneas que coincidan con el patrón “foo”. Por otro lado, conservar las llaves y omitir la sentencia print, hace un acción vacía que no hace nada (es decir, se imprimen líneas).

Muchos programas awk prácticos son conformado por sólo una o dos líneas. Para empezar, a continuación se presenta una colección de programas cortos pero útiles. Algunos de estos programas contienen construcciones que todavía no se han cubierto. (La descripción del programa awk va a dar una buena idea de lo que hace el mismo).

La mayoría de los ejemplos usa un archivo de datos llamado: datos. Esto es sólo un marcador de ejemplo, si se usan estos programas, los tenemos que sustituir por sus propios nombres de archivo de datos. Para referencia futura, hay que tener en cuenta que a menudo hay más de una manera de hacer las cosas con awk. En algún momento, es posible podamos mirar hacia atrás en estos ejemplos y ver si podemos encontrar diferentes maneras de hacer las mismas cosas que se muestran aquí:

Imprime la longitud de la línea de entrada más larga:

awk '{ if (length($0) > max) max = length($0) } END { print max }' BBS-list

Imprime cada línea que sea mayor a 44 carácteres:

awk 'length($0) > 44' BBS-list

Nota: La única regla que hay tiene una expresión relacional como su patrón y no tiene una acción. Por lo que se utiliza la acción predeterminada, la impresión del registro.

Imprime la longitud de la línea más larga de datos:

expand BBS-list | awk '{ if (x < length()) x = length() } END { print "maximum line length is " x }'

Nota: La entrada es procesada por el comando expand, que convierte TABs en espacios, por lo que en realidad es el ancho de comparación, son las columnas del margen de la derecha.

Imprime cada línea que al menos tiene un campo:

awk 'NF > 0' BBS-list

Nota: Es una manera fácil de eliminar las líneas en blanco de un archivo (para crear un nuevo archivo, sin las líneas en blanco).

Imprime siete números aleatorios entre 0 y 100, inclusive:

awk 'BEGIN { for (i = 1; i <= 7; i++) print int(101 * rand()) }'

Imprime el número total de bytes utilizado por BBS-list:

ls -l BBS-list | awk '{ x += $5 } END { print "total bytes: " x }'

Imprimir el número total de kilobytes utilizados por BBS-list:

ls -l BBS-list | awk '{ x += $5 } END { print "total K-bytes:", x / 1024 }'

Imprime una lista ordenada con todos los nombres de usuario:

awk -F: '{ print $1 }' /etc/passwd | sort

Cuenta el númerop de líneas en BBS-list:

awk 'END { print NR }' BBS-list

Imprime las líneas pares del archivo BBS-list:

awk 'NR % 2 == 0' BBS-list

Imprime las líneas impares del archivo BBS-list:

awk 'NR % 2 == 1' BBS-list

Un ejemplo con dos reglas

La utilidad awk lee los archivos de entrada de línea en línea, una a la vez. Para cada línea, awk trata los patrones de cada una de las reglas. Si varios patrones coinciden, entonces varias acciones se ejecutan en el orden en que aparecen en el programa awk. Si no hay patrones que coinciden, entonces no se corre ninguna acción.

Después de procesar todas las reglas que coinciden con la línea (que tal vez no hay), awk lee la siguiente línea. Esto continúa hasta que el programa llega al final del archivo. Por ejemplo, el siguiente programa awk contiene dos reglas:

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

La primera regla tiene la cadena “12” como patrón y “print $0” como la acción. La segunda regla tiene la cadena “21” como patrón y también tiene “print $0” como la acción. Cada acción de cada regla está encerrada dentro de su propio par de llaves.

Este programa imprime cada línea que contiene la cadena “12” o la cadena “21”. Si una línea contiene ambas cadenas, entonces se imprime dos veces, una por cada regla.

Esto es lo que ocurre si corremos este programa en nuestros dos archivos de datos de muestra (“inventory-shipped” y “BBS-list”):

awk '/12/ { print $0 }
     /21/ { print $0 }' BBS-list inventory-shipped
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

Nota: Observe que la línea que comienza con “sabafoo” en Lista-BBS fue impresa dos veces, una acción de impresión por cada regla.

Un ejemplo más complejo

Ahora que se entienden algunas tareas simples, echemos un vistazo a lo que los típicos programas awk pueden hacer. Este ejemplo muestra como awk puede ser utilizado para resumir, seleccionar y ordenar la salida de otra utilidad o comando:

LC_ALL=C ls -l | awk '$6 == "Nov" { sum += $5 }
                      END { print sum }'

Este comando imprime el número total de bytes de todos los archivos del directorio actual que fueron modificados por última vez en noviembre (de cualquier año). La parte “ls-l” de este ejemplo es un comando del sistema que da una lista de los archivos en un directorio, incluyendo el tamaño de cada archivo y la fecha de la última modificación del archivo. Su salida se puede ver así:

-rw-r--r--  1 arnold   user   1933 Nov  7 13:05 Makefile
-rw-r--r--  1 arnold   user  10809 Nov  7 13:03 awk.h
-rw-r--r--  1 arnold   user    983 Apr 13 12:14 awk.tab.h
-rw-r--r--  1 arnold   user  31869 Jun 15 12:20 awkgram.y
-rw-r--r--  1 arnold   user  22414 Nov  7 13:03 awk1.c
-rw-r--r--  1 arnold   user  37455 Nov  7 13:03 awk2.c
-rw-r--r--  1 arnold   user  27511 Dec  9 13:07 awk3.c
-rw-r--r--  1 arnold   user   7989 Nov  7 13:03 awk4.c

El primer campo contiene los permisos de lectura y escritura, el segundo campo contiene el número de enlaces hacia el archivo, y el tercer campo identifica el propietario del archivo. El cuarto campo identifica el grupo del archivo. El quinto campo contiene el tamaño del archivo en bytes. Los campos sexto, séptimo y octavo contienen el mes, el día y la hora, respectivamente, que significa la última modificación del archivo. Por último, el noveno campo contiene el nombre del archivo.

La expresión ‘$ 6 == “Nov”‘ en nuestro programa awk se encarga de comprobar si el sexto campo del resultado de “ls -l” coincide con la cadena “Nov”. Cada vez que una línea tiene la cadena “Nov” en su sexto campo, se realiza la acción “sum += $5”. Esto agrega el quinto campo (tamaño del archivo) a la variable de suma. Como resultado, cuando awk ha terminado de leer todas las líneas de entrada, la suma es el total de los tamaños de todos los archivos cuyas líneas coinciden con el patrón. (Esto funciona porque las variables awk se inicializan de manera automática en cero.)

Después de que la última línea de la salida de ls haya sido procesado, entonces la regla END ejecuta y muestra el valor de la suma. En este ejemplo, el valor de la suma es 80.600.

Estas técnicas más avanzadas de awk se tratan mejor en secciones posteriores. Pero antes de pasar a una programación más avanzada con awk, se recomienda conocer cómo awk interpreta una entrada (input) y cómo muestra el resultado (output). Mediante la manipulación de campos y el uso de sentencias de impresión, se pueden producir algunos informes muy útiles, con un aspecto impresionante.

Declaraciones awk Versus Líneas

Muy a menudo, cada línea en un programa awk es una sentencia separada o una regla separada, por ejemplo:

awk '/12/  { print $0 }
     /21/  { print $0 }' BBS-list inventory-shipped

Sin embargo, gawk ignora los saltos de línea después de cualquiera de los siguientes símbolos y palabras clave:

     ,    {    ?    :    ||    &&    do    else

Una línea nueva en cualquier otro punto se considera el final de la instrucción oi sentencia.

Si se desea dividir una única sentencia en dos líneas, en un punto donde línea nueva podría terminarla, se puede continuar si finalizamos la primera línea una barra invertida (‘\’). La barra invertida debe ser el último carácter en la línea, para que se reconozca como un carácter de continuación. Una barra invertida es permitida en cualquier lugar de la sentencia, incluso en el medio de una cadena o de una expresión regular. Por ejemplo:

awk '/This regular expression is too long, so continue it\
     on the next line/ { print $1 }'

Por lo general, no se ha usado la barra invertida de continuación en los programas de ejemplo.

gawk no establece límites en la longitud de la línea, por lo tanto la barra invertida de continuación no es estrictamente necesaria, sólo hace que los programas sean más legibles. Por esta misma razón, así como por razones de claridad, en los programas de ejemplo se han mantenido la mayoría de declaraciones de una manera cortas. La barra invertida de continuación es más útil cuando el programa awk está en un archivo fuente independiente, en lugar de ser usada en la línea de comandos.

Hay que tener en cuenta que muchas implementaciones de awk son más exigentes sobre dónde puede usar la barra invertida de continuación. Por ejemplo, no se permiten para dividir una cadena constante mediante la barra invertida de continuación. Por lo tanto, por portabilidad en los programas awk, lo mejor es no dividir las líneas en medio de una expresión regular o una cadena.

PRECAUCIÓN: La barra invertida de continuación no funciona como se describe con el C shell. Funciona para los programas awk en archivos y para programas “one-shot”, siempre y cuando se utiliza un shell compatible con POSIX, como el “Unix Bourne shell” o Bash. Pero el C shell C se comporta de manera diferente! Allí, se deben usar dos barras invertidas en una fila, seguido por un salto de línea. También hay que tener en cuenta que cuando se utiliza el C shell, cada línea nueva en un programa awk debe ser escapada con una barra invertida. Para ilustrar:

% awk 'BEGIN { \
?   print \\
?       "hello, world" \
? }'

En este caso, ‘%’ y ‘?’ Son del C shell (primary and secondary prompts), de forma análoga a la norma shell de ‘$’ y ‘>’.

Compare the previous example to how it is done with a POSIX-compliant shell:

Compare el ejemplo anterior, con la forma en que se hace en un shell compatible con POSIX:

$ awk 'BEGIN {
>   print \
>       "hello, world"
> }'

awk es un lenguaje orientado a líneas. Donde cada acción de cada regla tiene que comenzar en la misma línea que el patrón. Para que el patrón y la acción puedan quedar en líneas separadas, se debe utilizar la barra invertida de continuación, no hay otra opción.

Otra cosa a tener en cuenta es que la barra invertida de continuación y los comentarios no se mezclan. Tan pronto como awk ve “#” se inicia un comentario, por lo tanto awk ignora todo lo que se encuentre en el resto de la línea. Por ejemplo:

$ gawk 'BEGIN { print "dont panic" # a friendly \
>                                    BEGIN rule
> }'
error--> gawk: cmd. line:2:                BEGIN rule
error--> gawk: cmd. line:2:                ^ parse error

En el caso anterior, parece que la barra invertida continuaría el comentario en la siguiente línea. Sin embargo, la combinación de barra invertida con salto de línea ni siquiera se se llega a notar por awk, ya que sen encuentra “escondida” dentro del comentario. Por lo tanto, BEGIN se muestra como un error de sintaxis.

Cuando las sentencias awk en una regla son cortas, es posible que se desee poner más de una de ellas en una línea. Esto se logra mediante la separación de sentencias con un punto y coma (‘;’). Esto también se aplica a las mismas reglas. Por lo tanto, el programa que anterior:

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

también se podría escribir de esta manera:

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

NOTA: El requisito que establece que las reglas sobre la misma línea deben estar separadas por un punto y coma no estaba en el lenguaje awk original, más bien esto fue agregado por coherencia con el tratamiento de las sentencias dentro de una acción.

Cuándo utilizar awk

El lenguaje AWK es muy útil para la producción de informes desde grandes cantidades de datos en bruto.

Dado que los programas awk se interpretan, se puede evitar la parte de los ciclos lentos de compilación.

Si en un programa awk escribimos, digamos, más de cien líneas de código, es posible que se deba considerar el uso de un lenguaje de otro programación diferente.

Invocando awk

Hay dos formas de correr awk, ya sea con un programa explícito o con uno o más archivos de programa. Estas son las plantillas para ambos; elementos encerrados entre […] en estas plantillas son opcionales:

awk [opciones] -f 'programita' [--] archivo-de-datos ...
awk [opciones] [--] 'programita' archivo-de-datos ...

Además de las opciones tradicionales de una letra al estilo POSIX, gawk también es compatible con las opciones largas de GNU.

Es posible invocar awk con un programa vacío:

awk '' archivo-de-datos-1 archivo-de-datos-1

Opciones de línea de comandos

Las opciones comienzan con un guión y se componen por un solo carácter. Opciones largas al estilo GNU se componen de dos guiones y una palabra clave. La palabra clave puede ser abreviada, siempre y cuando la abreviatura permite la opción de ser identificada de forma única. Si la opción toma un argumento, entonces la palabra clave va inmediatamente seguida por un signo de igual (‘=’) y el valor del argumento, o la palabra clave y el valor del argumento se separan por espacios en blanco. Si una determinada opción con un valor viene dada más de una vez, entonces es el último valor el que cuenta.

Cada opción de gawk tiene tiene una opción corta y estilo POSIX correspondiente. Las opciones largas y cortas son intercambiables en todos los contextos. La siguiente lista describe las opciones requeridas por el estándar POSIX:

-F fs
--field-separator fs

Establece la variable FS a fs (FS = Separador de Campo).

-f source-file
--file source-file

Lee la fuente del programa awk desde un archivo. Esta opción puede ser usada múltiples veces; un programa awk puede consistir de la concatenación de los contenidos de cada archivo o script fuente que haya sido especificado.

-i source-file
--include source-file

Lee el código fuente de una librería. Esta opción equivale a usar la directiva ‘@ include‘ dentro de un programa. Esta opción es muy similar a la opción -f, pero existen dos diferencias importantes.

En primer lugar, si se usa la opción -i, no se carga el código fuente del programa si éste ya ha sido cargado previamente, mientras que la opción -f siempre cargará el archivo. En segundo lugar, porque esta opción está destinada para ser utilizada por código de librerías, gawk no reconoce tales archivos como la entrada principal del programa.

Por lo tanto, después de procesar una opción -i, gawk aún espera encontrar el código fuente principal a través de la opción -f o en la línea de comandos.

-v var=val
--assign var=val

Establece en la variable var el valor de val antes de que comience a correr el programa awk. Estos valores de variables pueden estar disponibles dentro de la regla BEGIN (ver otros argumentos).

La opción-v puede configurar sólo una variable, pero se puede usar múltiples veces, por ejemplo:

‘awk -v var1=val1 -v var2=val2 ...’

PRECAUCIÓN: El uso de la opción -v para establecer o configurar los valores de variables incorporadas (built-in) puede terminar en resultados sorprendentes. es probable que awk restablecerá los valores de esas variables en cualquier momento que lo necesite, haciendo caso omiso de cualquier valor predefinido que uno mismo pueda haber configurado previamente.

-N
--use-lc-numeric

Al analizar datos de entrada numéricos se usa el carácter de punto decimal que define la configuración regional (Locales).

-o[file]
--pretty-print[=file]

Habilita la impresión linda (pretty-printing) de los programas awk. De manera predeterminada, la salida de un programa queda en un archivo llamado awkprof.out

El argumento de archivo opcional permite especificar un nombre de archivo diferente para la salida. No se permite ningún espacio entre la opción -o y el archivo, si se suministra un archivo.

-O
--optimize

Activa unas optimizaciones en la representación interna del programa. Por el momento esto incluye sólo el simple “constant folding“.

-p[file]
--profile[=file]

Habilita el “Profiling” de programas awk. De forma predeterminada, los perfiles se crean en un archivo llamado awkprof.out. El argumento de archivo opcional permite especificar un nombre de archivo diferente para el archivo de perfil. No se permite ningún espacio entre la opción -p y el archivo, si se suministra archivo.

En el margen izquierdo, el perfil contiene recuentos de ejecución de cada sentencia del programa, y recuenta la llamada de funciones por cada función.

-P
--posix

Operar en modo estricto POSIX. Esto desactiva todas las extensiones gawk (tales como el doble guión) y desactiva todas las extensiones no permitidas por POSIX.

Además, se aplican las siguientes restricciones adicionales:

  • Los fines de línea no actúan como espacios en blanco para separar los campos cuando FS es igual a un solo espacio.
  • Los fines de línea no se permiten después de “?” o “:
  • Si se especifica la opción -Ft en la línea de comando, ésto no establece el valor de FS a ser un solo carácter de tabulación.
  • Utiliza el carácter de punto decimal de la localización (Locale) para analizar los datos de entrada.

Si se proporcionan tanto –tradicional y –posix en la línea de comandos, –posix tiene prioridad. Además gawk emite un aviso si se suministran las dos opciones.

-r
--re-interval

Permite expresiones de intervalo (Operadores Regexp) en las expresiones regulares. Ahora éste es el comportamiento predeterminado de gawk. Sin embargo, esta opción se mantiene tanto por compatibilidad hacia atrás, y para su uso en combinación con la opción –tradicional.

-S
--sandbox

Desactiva la función system(), redirecciones de entrada con getline, redirecciones de salida con print y printf, y extensiones dinámicas. Esto es útil cuando se quiere correr scripts awk de fuentes cuestionables, para asegurar de que los scripts no pueden acceder a nuestro sistema (que no sea el archivo de datos de entrada especificado).

-t
--lint-old

Avisar sobre “constructs” que no están disponibles en la versión original de awk, desde la versión 7 de Unix (ver V7/SVR3.1).

-V
--version

Imprime información sobre la versión particular de gawk. Esto permite determinar si la copia de gawk se encuentra al día con respecto a la que distribuye la Free Software Foundation.

También es muy útil para cooperar con reportes, cuando se presentan errores (Bugs).

Siempre y cuando se haya suministrado el texto del programa, cualquier otra opción se marca como no válida, con un mensaje de advertencia, de lo contrario se pasan por alto.

En el modo de compatibilidad, como un caso especial, si el valor de fs suministrado desde la opción -F es “t“, entonces FS se establece en el carácter de tabulación “\ t“. Esto es cierto sólo para –tradicional y no para –posix.

Si la opción -f se usa más de una vez en la línea de comandos, awk lee su fuente de programas desde todos los archivos especificados, como si hubieran sido concatenados en un sólo archivo grande. Esto es útil para la creación de bibliotecas con funciones awk. Estas funciones pueden escribirse una sola vez y luego y ser recuperadas desde un ínico lugar estándar, en lugar de tener que ser incluido de manera duplicada en cada programa individual (los nombres de funciones deben ser únicos.)

Con el awk estándar, se pueden seguir utilizando funciones de biblioteca, incluso si se introduce el programa desde el terminal, al “-f /dev/tty“. Después de escribir el programa, para salir pulse Ctrl-d (el carácter de fin de archivo). (También puede usar “-f –” para leer el código fuente del programa desde la entrada estándar, pero luego no se puede utilizar la entrada estándar como fuente de entrada de datos.)

Debido a que es torpe mezclar los mecanismos awk estándar para mezclar programas awk desde archivos y desde la línea de comandos, gawk proporciona la opción –source. Esto no requiere de concatenar (pre-empt) la entrada estándar para el código fuente, sino que permite mezclar de manera fácil la línea de comandos y el código fuente de biblioteca (ver Variable AWKPATH). La opción –source también se puede usar múltiples veces desde la línea de comandos.

Si no se especifica la opción -f o la opción –source, entonces gawk utiliza el primer argumento no-opción desde la línea de comandos como texto del código fuente del programa.

Si la variable de entorno o ambiente POSIXLY_CORRECT existe, entonces gawk se comporta en modo estricto POSIX, exactamente como si se hubiera suministrado la opción –posix desde línea de comandos.

Muchos de los programas GNU buscan esta variable de entorno para suprimir las extensiones que entren en conflicto con POSIX, pero gawk se comporta de manera diferente: suprime todas las extensiones, incluso aquellas que no entran en conflicto con POSIX, y se comporta en modo estricto POSIX.

Si se suministra la opción –lint desde la línea de comandos y gawk activa el modo POSIX porque se configura POSIXLY_CORRECT, entonces se emite un mensaje de advertencia indicando que el modo POSIX está en vigor. Esta variable se puede establecer en el archivo de inicio del Shell.

Para ser compatible con Bourne Shell (como Bash), se deben agregar estas líneas al archivo .profile en nuestro propio directorio principal (home):

POSIXLY_CORRECT=true
export POSIXLY_CORRECT

Para tener compatibilidad con un C shell, se debe agregar esta línea en el archivo de inicio de sesión .login en nuestro propio directorio principal (home):

setenv POSIXLY_CORRECT true

NOTA: No se recomienda tener configurado POSIXLY_CORRECT en el uso diario, pero es bueno para probar la portabilidad de los programas en otros entornos.

Otros argumentos desde la línea de comandos

Los argumentos adicionales desde la línea de comandos se tratan normalmente como archivos de entrada que deben ser procesados en el orden especificado. Sin embargo, si un argumento tiene la forma var=value, se asigna el valor value a la variable var. No se especifica un archivo en lo absoluto.

Todos estos argumentos son disponibles para un programa AWK desde el arreglo ARGV (ver Variables Buit-in o integradas). Las opciones de línea de comandos y el texto del programa (si está presente) se omiten desde ARGV. Todos los demás argumentos, incluyendo las asignaciones de variables, sí se incluyen. A medida que se procesa cada elemento del arreglo ARGV, gawk establece la variable ARGIND al índice en ARGV del elemento actual.

La distinción entre los argumentos de nombre de archivo y argumentos de asignación de variables se realiza cuando awk está a punto de abrir el siguiente archivo de entrada. En éste momento de la ejecución, comprueba el nombre awk verfica el nombfre de archivo para ver si realmente es una asignación de variable, si es así, awk establece la variable en lugar de leer un archivo.

Por lo tanto, las variables reciben los valores dados después de que todos los archivos especificados previamente hayan sido leídos. En particular, los valores de las variables asignadas de esta manera no se encuentran disponibles dentro de una regla BEGIN (ver BEGIN/END), porque tales reglas se ejecutan antes de que awk comienza a escanear la lista de argumentos.

Los valores de las variables que aparecen en la línea de comandos se procesan por “escape sequences“.

En algunas implementaciones anteriores de awk, cuando una asignación de variables se producía antes que los nombres de archivo, la asignación iba a suceder antes de que la regla BEGIN sea ejecutada. El comportamiento de awk fue tan incompatible; algunas asignaciones desde la línea de comandos estaban disponibles dentro de la regla BEGIN, mientras que otras no lo eran. Desgraciadamente, muchas aplicaciones llegaron a depender de esta “característica”. Cuando awk fue cambiado para ser más consistente, se agrego la opción -v para dar cabida a las aplicaciones que dependan de este viejo comportamiento.

La función de asignación de variables es muy útil para asignar a variables tales como RS, OFS, y ORS, que controla los formatos de entrada y salida antes de escanear los archivos de datos. También es útil para controlar el estado si se requieren múltiples pasadas sobre un archivo de datos. Por ejemplo:

awk 'pass == 1  { pass 1 stuff }
     pass == 2  { pass 2 stuff }' pass=1 mydata pass=2 mydata

Dada la función de asignación de variables, la opción -F para ajustar el valor de FS no es estrictamente necesario. Se mantiene por compatibilidad histórica.

Nombramiento de entrada estándar

A menudo, es posible que se desea leer la entrada estándar, junto con otros archivos. Por ejemplo, es posible que se necesita leer un archivo, leer la entrada estándar procedente de un “pipe“, y luego leer otro archivo.

La forma de nombrar la entrada estándar, con todas las versiones de awk, es el uso de un sólo guión, ‘-‘. Por ejemplo:

un_comando | awk -f programita.awk archivo-1 - archivo-2

Aquí, awk primero lee archivo-1, seguidamente lee la salida de un_comando, y finalmente lee archivo-2.

También se puede utilizar “” para nombrar la entrada estándar al leer archivos con getline (ver Getline/File).

Además, gawk permite especificar el nombre de archivo especial /dev/stdin, tanto en la línea de comandos como con getline. Otras versiones de awk también soportan esto, pero no es estándar. (Algunos sistemas operativos ofrecen el archivo /dev/stdin en su sistema de archivos, sin embargo, gawk siempre procesa este nombre de archivo él mismo.)

La variable de entorno AWKPATH

En la mayoría de las implementaciones de awk, se debe proporcionar un nombre de ruta precisa para cada archivo de programa, a menos que el archivo se encuentre en el directorio actual. Pero en gawk, si el nombre de archivo suministrado con la opción -f o con la opción -i no contiene un “/“, entonces gawk busca en una lista de directorios (conocida como la ruta de búsqueda), uno por uno, en busca de un archivo con el nombre especificado.

La ruta de búsqueda es una cadena conformada por nombres de directorios separados por dos puntos. gawk obtiene su ruta de búsqueda desde la variable de entorno AWKPATH. Si la variable no existe, gawk utiliza una ruta por defecto “.:/usr/local/share/awk

La función de ruta de búsqueda es esencial en la construcción de bibliotecas útiles con funciones awk. Los archivos de biblioteca se pueden colocar en un directorio estándar en la ruta predeterminada para que se puedan especificar desde la línea de comando mediante un nombre de archivo corto. De lo contrario, el nombre completo de archivo tendría que ser escrito para cada archivo.

Mediante el uso de la opción -i, o de la opción –source y la opción -f, los programas awk desde la línea de comandos pueden utilizar las características de archivos de biblioteca awk. La ruta de búsqueda no se lleva a cabo si gawk está en modo de compatibilidad. Esto es cierto para ambos –tradicional y –posix.

Si el código fuente no se encuentra después de la búsqueda inicial, de nuevo se busca el camino después de el sufijo por defecto “.awk” al nombre de archivo.

NOTA: Para incluir el directorio actual en la ruta, ya sea colocando explícitamente la ruta o escribiendo una entrada nula en la ruta. (Una entrada nula se indica empeando y finalizando la ruta con dos puntos, o colocando dos signos de dos puntos seguidos “::“.) Este mecanismo de búsqueda de ruta es similar a la del Shell.
Sin embargo, gawk siempre busca en el directorio actual antes de buscar en AWKPATH, por lo que no hay ninguna razón real para incluir el directorio actual en la ruta de búsqueda.

Si AWKPATH no se define en el entorno, gawk pone su ruta de búsqueda predeterminada en ENVIRON[“AWKPATH”]. Esto hace que sea fácil determinar la ruta de búsqueda real que gawk utilizará dentro de un programa awk.

Aunque es posible cambiar ENVIRON[“AWKPATH”] dentro de un programa awk, esto no tiene ningún efecto en el comportamiento del programa en ejecución. Lo que sí tiene sentido es que la variable de entorno AWKPATH se utiliza para encontrar los archivos de código fuente del programa. Una vez que el programa se está ejecutando, todos los archivos ya han sido encontrados, por lo que gawk ya no necesita utilizar AWKPATH.

La variable de entorno AWKLIBPATH

La variable de entorno AWKLIBPATH es similar a la variable AWKPATH, pera esta se usa para buscar bibliotecas compartidas que hayan sido especificadas con la opción -l en lugar de los archivos de origen. Si no se encuentra la biblioteca, entonces se busca de nuevo la ruta después de agregar el sufijo de biblioteca compartida respectivo de la plataforma. Por ejemplo, en los sistemas GNU/Linux se utiliza el sufijo de plataforma.so“. La ruta de búsqueda especificada también se utiliza para las bibliotecas cargadas mediante la palabra clave “@ load

Otras variables de entorno

Es posible afectar el comportamiento de gawk con el uso de otras variables de entorno, pero son más especializadas. Los que se mencionan en la siguiente lista están destinados a ser utilizados por usuarios comunes.

POSIXLY_CORRECT

Le dice a gawk que cambie a modo de compatibilidad POSIX, desactivando todas las extensiones tradicionales y GNU.

GAWK_SOCK_RETRIES

Controla la cantidad de tiempo que gawk intentará una conexión TCP/IP bidireccional (socket) antes de abandonar. Ver redes TCP/IP.

GAWK_MSEC_SLEEP

Especifica el intervalo entre reintentos de conexión, en milisegundos. En los sistemas que no soporten la llamada de sistema usleep(), el valor se redondea a un número entero de segundos.

GAWK_READ_TIMEOUT

Especifica el tiempo, en milisegundos, para que gawk que espere por la entrada antes de regresar con un error.

“Testing and Tuning”

Las variables de entorno siguientes son apropiados para los desarrolladores de gawk, para realizar pruebas y afinar. Se encuentran sujetas a cambios. Las variables son:

AVG_CHAIN_MAX

El número promedio de items que gawk mantendrá en una cadena hash en la gestión de arreglos o matrices.

AWK_HASH

Si esta variable existe con un valor de “gst”, gawk entonces usará la función hash de GNU Smalltalk para la gestión de arreglos o matrices. Esta función puede ser marginalmente más rápida que la función estándar.

AWKREADFUNC

Si la variable existe, gawk entonces leerá los archivos de código fuente una línea a la vez, en lugar de leerlos en bloques. Esto existe para depurar problemas en sistemas de archivos en sistemas operativos que no son POSIX donde I/O se lleva a cabo en los registros, no en bloques.

GAWK_NO_DFA

Si la variable existe, gawk no utiliza el “DFA regexp matcher” para el tipo de pruebas “does it match”. Esto puede causar que gawk sea más lento. Su propósito es ayudar a aislar las diferencias entre los dos comparadores de expresiones regulares que gawk utiliza internamente. (Se supone que no hay diferencias, pero en ocasiones la teoría y la práctica no se coordinan entre sí.)

GAWK_STACKSIZE

Esto especifica la cantidad en que gawk debe crecer su pila (stack) de evaluación interna, cuando sea necesario.

TIDYMEM

Si la variable existe, gawk usa las llamadas de librería mtrace() desde GNU LIBC para ayudar a localizar posibles pérdidas de memoria.

Estado de salida de gawk (Exit Status)

Si la instrucción de salida se utiliza con un valor, entonces gawk termina devolviendo ese valor numérico.

De lo contrario, si no hay problemas durante la ejecución, gawk termina con el valor de la constante C, EXIT_SUCCESS. Por lo general el valor es cero = 0.

Si se produce un error, gawk termina con el valor de la constante C, EXIT_FAILURE. Por lo general el valor es uno = 1.

Si gawk termina por causa de un error grave, el estado de salida es 2. En los sistemas no POSIX, este valor puede ser asignado a EXIT_FAILURE.

Incluyendo otros archivos (@include)

Esta sección describe una característica que es específica de gawk.

La palabra clave “@include” se utiliza para leer archivos awk de origen externos. Esto permite dividir grandes archivos de origen awk en partes manejables y más pequeñas. También permite reutilizar el código awk desde varios scripts awk.

En otras palabras, se puede agrupar funciones awk, en archivos externos, con funciones esenciales para llevar a cabo ciertas tareas específicas.

Estos archivos pueden ser utilizados como bibliotecas de funciones, usando la palabra clave “@include” en combinación con la variable de entorno AWKPATH.

Hay que tener en cuenta que los archivos de origen también pueden incluidos con el uso de la opción -i.

Veamos un ejemplo. Vamos a empezar con dos scripts awk (trivial), es decir, con el script llamado test1 y con el script llamado test2. A continuación, el script test1 contiene:

BEGIN {
       print "This is script test1."
}

y el script test2 contiene:

@include "test1"

BEGIN {
       print "This is script test2."
}

Correr gawk con el script test2 produce produce el siguiente resultado:

gawk -f test2
This is script test1.
This is script test2.

gawk corre el script test2 que a su vez incluye test1 utilizando la palabra clave “@include“. Por lo tanto, para incluir archivos awk de origen externo, se tiene que utilizar “@include” seguido por el nombre del archivo que se quiere incluir, el nombre debe ir entre comillas dobles.

NOTA: Hay que recordar que esto es un “language construct” por lo que el nombre del archivo no puede ser una cadena variable, pero sí una sola cadena literal entre comillas dobles.

Los archivos que se vayan a incluir pueden estar anidados, por ejemplo, si incluimos un tercer archivo llamado script test3 con el siguiente contenido:

@include "test2"

BEGIN {
       print "This is script test3."
}

Correr gawk con el script test3 produce el siguiente resultado:

gawk -f test3
This is file test1.
This is file test2.
This is file test3.

Por supuesto que el nombre de archivo puede ser una ruta. Por ejemplo:

@include "../io_funcs"

también se puede usar algo como:

@include "/usr/awklib/network"

ambos usos son válidos. La variable de entorno AWKPATH puede ser muy útil cuando se utiliza “@include“. Además las mismas reglas que aplican para el uso de la variable AWKPATH en búsquedas de archivos desde la línea de comandos también se aplican a “@include“.

Es muy útil para la construcción de bibliotecas con funciones gawk. Si se tiene un gran script con varias funciones awk útiles y generales, entonces se puede dividir en archivos de bibliotecas, además podemos colocar los archivos en un directorio especial.

Entonces se puede incluir esas nuevas “bibliotecas”, ya sea utilizando la ruta completa de los archivos, o configurando la variable de entorno AWKPATH para luego ser usada con “@include” con sólo la parte del archivo de la ruta completa. Por supuesto, se puede tener más de un directorio especial para conservar los archivos de biblioteca, mientras más complejo sea el entorno de trabajo, más directorios van a ser necesarios de organizar para los archivos que vayan a necesitar ser incluidos.

Gracias a que se puede utilizar mútiples veces la opción -f , el mecanismo “@include” no es estrictamente necesario. Sin embargo, la palabra clave “@include” puede ayudar en la construcción de programas gawk autónomos, lo que reduce la necesidad de escribir líneas de comandos complejos y tediosos. En lo particular, “@include” es muy útil cuando se escriben scripts CGI que se corren desde páginas web.

Como se mencionó, para la Variable AWKPATH, siempre se busca primero en el directorio actual por los archivos de origen, antes de buscar en AWKPATH, y esto también aplica para los archivos nombrados con “@include“.

Cargando bibliotecas compartidas (@load)

Esta sección describe una característica que es específica de gawk.

La palabra clave “@load” se puede utilizar para leer bibliotecas compartidas awk externas. Esto permite enlazar código compilado que ofrece un mayor rendimiento y/o dar acceso a características extendidas que no son soportadas por el lenguaje awk.

La variable AWKLIBPATH se utiliza para buscar por bibliotecas compartidas. Usar “@load” equivale a utilizar la opción -l desde la línea de comandos.

Si inicialmente la biblioteca compartida no se encuentra en AWKLIBPATH, otra búsqueda se lleva a cabo, agregando el sufijo de plataforma por defecto de biblioteca compartida para el nombre de archivo. Por ejemplo, en los sistemas GNU/Linux se utiliza el sufijo de plataforma “.so“.

gawk '@load "ordchr"; BEGIN {print chr(65)}'

Esto es equivalente a:

gawk -lordchr 'BEGIN {print chr(65)}'

Para el uso desde la línea de comandos, es más conveniente la opción -l, pero “@load” es útil para incorporar dentro de un archivo de origen awk que requiere el acceso a una biblioteca compartida.

Las Extensiones Dinámicas, describen cómo escribir extensiones (en C o C++) se pueden cargar con cualquiera “@load” o la opción -l.

Funciones y opciones obsoletas

En esta sección se describen las características y opciones desde la línea de comandos, de versiones anteriores de gawk que, o bien no están disponibles en la versión actual o que todavía están soportadas, pero en desuso (lo que significa que no estarán disponibles en la próxima versión).

Los archivos relacionados con procesos especiales /dev/pid, /dev/ppid, /dev/pgrpid y /dev/user fueron desaprobados en gawk 3.1, pero todavía funcionan. Desde la versión 4.0, ya no son interpretados por gawk. (Mejor utilizar PROCINFO.)

Opciones y características sin documentación

Use the Source, Luke!

--Obi-Wan.

Esta sección ha quedado en blanco de manera intencional.

Expresiones regulares

Una expresión regular (RegExp) es una manera de describir un conjunto de cadenas. Dado que las expresiones regulares son una parte fundamental de la programación en awk, su formato y uso merecen un capítulo aparte.

Una expresión regular encerrada entre barras (‘/’) es un patrón de awk que puede coincidir con cada registro de entrada cuyo texto pertenece a ese conjunto. La expresión regular más simple es una secuencia de letras, números o ambos. Como esta expresión regular puede coincidir con cualquier cadena que contenga esa secuencia. Por ejemplo, la expresión regular “foo” coincide con cualquier cadena que contiene “foo”. Por lo tanto, el patrón /foo/ coincide con cualquier registro de entrada que contiene los tres carácteres “foo” en cualquier parte del registro. Otros tipos de expresiones regulares también permiten especificar clases de cadenas más complicadas.

Inicialmente, los ejemplos que vienen a continuación son simples. De acuerdo a como se explica más acerca de cómo funcionan las expresiones regulares, se van a ir presentando casos más complicados.

Cómo utilizar expresiones regulares

Una expresión regular se puede utilizar como un patrón dentro de barras “/“. Entonces la expresión regular se compara con el texto completo de cada registro. (Normalmente, sólo tiene que coincidir con una parte del texto con el fin de tener éxito.)

Por ejemplo, a continuación se imprime el segundo campo de cada registro que contiene la cadena “foo” en cualquier parte de ella:

awk '/foo/ { print $2 }' BBS-list

Las expresiones regulares también se pueden utilizar en la coincidencia de expresiones. Estas expresiones permiten especificar la cadena en contra a coincidir, no tiene que ser todo el registro de entrada actual.

Los dos operadores “~” y “!~” realizan comparaciones de expresiones regulares. Expresiones que utilizan estos operadores pueden ser utilizados como patrones, o en “if“, “while“, “for“, y sentencias “do“. Por ejemplo:

exp ~ /regexp/

es cierto si la expresión exp (tomada como una cadena) coincide con regexp.

En el siguiente ejemplo coincide, o selecciona, todos los registros de entrada con la letra mayúscula “J” en algún lugar en el primer campo:

awk '$1 ~ /J/' inventory-shipped

Lo mismo hace lo siguiente:

awk '{ if ($1 ~ /J/) print }' inventory-shipped

El siguiente ejemplo es verdadero si la expresión exp (tomada como una cadena de caracteres) no coincide con regexp:

exp !~ /regexp/

En el siguiente ejemplo coincide, o selecciona, todos los registros de entrada cuyo primer campo no contiene la letra mayúscula “J”:

awk '$1 !~ /J/' inventory-shipped

Cuando una expresión regular está encerrada entre barras, como /foo/, esto lo podemos llamar una expresión regular constante, al igual que 5,27 es una constante numérica y “foo” es una constante de cadena.

Secuencias de escape

Un uso de una secuencia de escape es incluir un carácter de comillas dobles dentro de una cadena constante. Debido a que una comilla doble terrmina una cadena, se debe usar \” para representar un carácter de comillas dobles real como parte de la cadena, por ejemplo:

awk 'BEGIN { print "He said \"hi!\" to her." }'

El propio carácter de barra invertida es otro carácter que no se puede incluir de manera normal, se debe escribir “\\” para colocar un barra invertida en la cadena o expresión regular. por lo tanto, la cadena cuyo contenido son los dos caracteres ” y \ se debe escribir \”\\.

Otras secuencias de escape representan caracteres que se imprimen, como TAB o nueva línea. Aunque nada impide introducir carácteres que no se imprimen directamente en una constante de cadena o en una expresión regular constante, es posible que no se vean muy bien.

La siguiente tabla muestra todas las secuencias de escape utilizadas en awk y lo que representan. A menos que se indique lo contrario, todas estas secuencias de escape se aplican tanto a las constantes de cadena y a las constantes de expresiones regulares:

\\

Literalmente una barra invertida (backslash) “\”.

\a

El carácter de “alerta”, Ctrl-g, el código ASCII 7 (BEL).
(Por lo general, produce una especie de ruido audible.)

\b

Retroceso (backspace), Ctrl-h, el código ASCII 8 (BS).

\f

Formfeed, Ctrl-l, el código ASCII 12 (FF).

\n

Nueva línea (newline), Ctrl-j, el código ASCII 10 (LF).

\r

Retorno de carro (carriage return), Ctrl-m, el código ASCII 13 (CR).

\t

TAB horizontal, Ctrl-i, el código ASCII 9 (HT).

\v

Tab vertical, Ctrl-k, el código ASCII 11 (VT).

\nnn

El valor octal nnn, donde nnn es sinónimo de 1 a 3 dígitos entre 0 y 7. Por ejemplo, el código para el carácter ASCII ESC (escape) es “\033”

\xhh...

El valor hexadecimal hh, donde hh representa una secuencia de dígitos hexadecimales (0-9, o bien A-F o a-f). Al igual que la misma construcción que sucede en ISO C, la secuencia de escape continúa hasta que se vea el primer dígito no-hexadecimal. Sin embargo, el uso de más de dos dígitos hexadecimales produce resultados no definidos. (La secuencia de escape “\x” no está permitida en POSIX awk.)

\/

Literalmente una barra diagonal (slash) (necesaria sólo para las constantes de expresiones regulares). Esta secuencia se utiliza cuando se quiere escribir una constante de expresión regular que contiene una barra (slash). Debido a que la expresión regular está delimitada por barras, es que se necesita escapar la barra que forme parte del patrón, con el fin de decirle a awk que procese el resto de la expresión regular.

\"

Literalmente una comilla doble (necesaria sólo para las constantes de cadena). Esta secuencia se utiliza cuando se quiere escribir una constante de cadena que contiene comillas dobles. Dado que la cadena está delimitada por comillas dobles, es que se debe escapar la comilla que forma parte de la cadena, con el fin de decirle a awk procese el resto de la cadena.

En gawk, una serie de secuencias de dos caracteres adicionales que comienzan con una barra invertida tiene un significado especial en expresiones regulares.

En una expresión regular, una barra invertida antes de cualquier carácter que no esté en la lista anterior y no aparece en expresiones regulares de Operadores GNU, significa que el siguiente carácter debe ser tomado de manera literal, aunque normalmente sea un operador de expresión regular. Por ejemplo, /a\+b/ coincide con los tres carácteres “a+b“.

Para mantener una portabilidad completa, no utilice una barra invertida antes de cualquier carácter que no aparezca en la lista anterior.

En resumen

Las secuencias de escape en la tabla anterior se procesan siempre de primero, tanto para cadenas constantes como para constantes de expresiones regulares. Esto ocurre muy temprano, tan pronto como awk lee un programa.

gawk procesa ambas, las constantes de expresiones regulares y las expresiones regulares dinámicas, para los operadores especiales listados en los Operadores RegExp GNU.

Una barra invertida antes de cualquier otro carácter significa que se va a tratar a ese carácter de manera literal.

Barra invertida antes carácteres normales

Si se coloca una barra invertida en una cadena constante antes de algo qu eno sea uno de los caráccteres mencionados arriba, awk POSIX awk deliberadamente permite lo que pase como indefinido. Hay dos opciones:

Quite las barras invertidas “Strip the backslash out”

Esto es lo que awk de Brian Kernighan y gawk hacen. Por ejemplo, “a\qc” es el mismo que “aqc“. (Debido a que este es un error muy fácil cometer, al menos gawk advierte al respecto.) Considere FS = “[ \t]+\|[ \t]+” para utilizar barras verticales rodeadas por espacios en blanco como separador de campo. Por esta razón deberían haber dos barras invertidas en la cadena: FS = “[ \t]+\\|[ \t]+”

Deja sola la barra invertida

Algunas otras implementaciones de awk hacen esto. En tales implementaciones, si se escribe “a\qc” es lo mismo que escribir “a\\qc“.

Secuencias de escape para Metacaracteres

Supongamos que se usa un escape octal o un escape hexadecimal para representar metacaracter en una expresión regular. ¿awk trata el carácter como un carácter literal o como un operador de expresión regular?

Históricamente, estos carácteres fueron tomadas literalmente. Sin embargo, el estándar POSIX indica que deben ser tratados como metacaracteres reales, que es lo que gawk hace.

En modo de compatibilidad, gawk trata a los carácteres representados por el escape octal y el escape hexadecimal, literalmente, cuando se utiliza en constantes de expresiones regulares. Por lo tanto, /a\52b/ es equivalente a /a\*b/.

Operadores de expresiones regulares

Se puede combinar expresiones regulares con carácteres especiales, llamados operadores de expresiones regulares o metacaracteres, esto para aumentar la potencia y la versatilidad de las expresiones regulares.

Las secuencias de escape descritas anteriormente, son válidas dentro de una expresión regular. Se introducen mediante un “\” y se reconocen y convierten en su correspondientes caracteres reales, esto como el primer paso en el procesamiento de expresiones regulares (RegExp).

A continuación una lista de los metacaracteres. Todos los caracteres que no son secuencias de escape y no están listados en la tabla por sí mismos:

\

Se utiliza para suprimir el significado especial de un carácter cuando hay coincidencia. Por ejemplo, “\$” coincide con el carácter “$“.

^

Se coincide con el inicio de una cadena. Por ejemplo, “^@chapter” coincide con “@chapter” al principio de una cadena y se puede utilizar para identificar capítulo iniciales en los archivos fuente del tipo Texinfo.

El “^” se conoce como un ancla, porque fija el patrón para que coincida sólo al inicio de la cadena.

Es importante recordar que “^” no coincida con el comienzo de una línea integrada en una cadena. La condición no es verdadera en el siguiente ejemplo:

if ("line1\nLINE 2" ~ /^L/) ...
$

Es similar a “^“, pero sólo coincide con al final de una cadena. Por ejemplo, “p$” coincide con un registro que termine en una “p“. El ancla “$” no coincide con el final de una línea incrustada en una cadena. La condición en el siguiente ejemplo no es verdadera:

if ("line1\nLINE 2" ~ /1$/) ...
. (period)

Coincide con cualquier carácter único, incluido el carácter de nueva línea. Por ejemplo “.P” coincide con cualquier carácter en la cadena seguido por una “P“. Utilizando concatenación, podemos hacer una expresión regular como “U.A“, que coincide con cualquier secuencia de tres caracteres que comienza con “U” y termina con una “A“.

En el modo estricto POSIX, “.” no coincide con el carácter NUL, el cual es un caracter con todos sus bits iguales a cero. Por lo contrario, NUL es otro caracter más. Otras versiones de awk pueden ser incapaces coincidir con el carácter NUL.

[…]

Se le conoce como un ua expressión entre corchetes. Coincide con cualquiera de los caracteres que se incluyen entre los corchetes cuadrados. Por ejemplo, “[MVX]” coincide con cualquiera de los caracteres “M“, “V” o “X” en una cadena.

[^ …]

Se le conoce como el complemento de una expresión de corchetes. El primer carácter después del “[” debe ser un “^“. Coincide con cualquier carácter excepto con los que sencuentran entre los corchetes cuadrados. Por ejemplo “[^awk]” coincide con cualquier carácter que no sea una “a“, “w“, o “k“.

|

Este es el operador de alternancia, se utiliza para especificar alternativas.

El operador “|” tiene la prioridad más baja entre todos los operadores de expresiones regulares. Por ejemplo “^P|[[:digit:]]” coincide con cualquier cadena que coincide ya sea “^P” o “[[:digit:]]“. Esto significa que coincide con cualquier cadena que comience con una “P” o que contiene un dígito.

La alternancia se aplica a las expresiones regulares más grandes posibles en cada lado.

(…)

En expresiones regulares los paréntesis se utilizan para agrupar, en aritmética también. Se pueden utilizar para concatenar expresiones regulares que contienen el operador de alternancia “|“. Por ejemplo, “@(samp|code)\{[^}]+\}” coincide tanto con “@code{foo}” como con “@samp{bar}“. (Estas son secuencias de control en formato Texinfo. El “+” se explica más adelante.)

*

Este símbolo significa que la expresión regular anterior debe repetirse tantas veces como sea necesario para encontrar una coincidencia. Por ejemplo, “ph*” aplica el símbolo “*” a la “h” anterior y busca la coincidencias de una “p” seguida por cualquier número de h. Además también coincide con una sóla “p” si no hay “h” presentes.

El “*” repite la más pequeña expresión posible anterior. (Se utilizan paréntesis para repetir toda una expresión mayor.) Se encuentran tantas repeticiones como sea posible. Por ejemplo, “awk ‘/\(c[ad][ad]*r x\)/ { print }’ sample” imprime cada registro del archivo “sample” que contiene una cadena de la forma “(car x)“, “(cdr x)“, “(cadr x)” y así sucesivamente. Observe el escape de los paréntesis, precedidos por barras invertidas (backslashes).

+

Este operador es similar a “*“, excepto que la expresión precedente debe coincidir al menos una vez. Esto significa que “wh+y” coincidiría con “why” y con “whhy” pero no con “wy“, mientras que “wh*y” si puede coincidir con las tres cadenas. La siguiente, es una manera más simple de escribir el último ejemplo “*“:

awk '/\(c[ad]+r x\)/ { print }' sample
?

Este operador es similar a “*“, excepto que la expresión anterior puede coincidir una vez o ninguna en absoluto. Por ejemplo, “fe?d” coincide con “fed” y “fd“, pero nada más.

{n}
{n,}
{n,m}

Uno o dos números dentro de llaves denotan una expresión de intervalo. Si hay un número entre las llaves, la expresión regular anterior se repite n veces. Si hay dos números separados por una coma, la expresión regular anterior se repite desde n hasta m veces. Si hay un número seguido de una coma, entonces la expresión regular anterior se repite al menos n veces:

wh{3}y

Coincide con “whhhy“, pero no con “why” o con “whhhhy”.

wh{3,5}y

Coincide sólo con “whhhy“, “whhhhy” o “whhhhhy“, nada más.

wh{2,}y

Coincide con “whhy” o “whhhy“, y así sucesivamente.

Las expresiones de intervalos no se encuentran disponibles de manera tradicional en awk. Esto se agregó como parte del estándar POSIX para que awk y egrep fueran coherentes entre sí.

En un principio, porque los programas viejos usaban “{” y “}” en las constantes de expresiones regulares, gawk no coincide con expresiones de intervalo en las expresiones regulares.

Sin embargo, a partir de la versión 4.0, gawk por defecto sí coincide con las expresiones de intervalo. Esto se debe a que la compatibilidad con POSIX se ha vuelto más importante para la mayoría de usuarios gawk que la compatibilidad con programas viejos.

Para los programas que utilizan “{” y “}” en las constantes de expresiones regulares, es una buena práctica siempre escaparlas con una barra invertida. Por eso las constantes de expresiones regulares son válidas y funcionan de la manera que se quiere, usando cualquier versión de awk.

Por último, cuando “{” y “}” aparecen en contantes de expresiones regulares de una manera que no puede ser interpretada como una expresión de intervalo (por ejemplo /q{a}/), entonces se representan a sí mismos.

En expresiones regulares, los operadores “*“, “+” y el “?“, así como las llaves “{” y “}“, tienen la prioridad más alta, seguido por la concatenación, y finalmente por “|“. Al igual que en la aritmética, los paréntesis pueden cambiar cómo se agrupan los operadores.

En POSIX awk y gawk, los operadores “*“, “+” y el “?” se representan a sí mismos cuando no hay nada en la expresión regular que les precede. Por ejemplo, /+/ coincide literalmente con el signo de más. Sin embargo, muchas otras versiones de awk tratan este uso como un error de sintaxis.

Si gawk está en modo de compatibilidad, las expresiones de intervalo no se encuentran disponibles en las expresiones regulares.

Linux – Arbol de directorios y tamaños de archivos

“tree” usando “ls”:

ls -R /etc | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\//--/g' -e 's/^/   /' -e 's/-/|/'

Los archivos/directorios de mayor tamaño dentro de /var:

du -a /var | sort -nr | head -n20
for i in G M K; do du -ah /var | grep [0-9]$i | sort -k1 -nr; done | head -n20

Sólo los archivos de mayor tamaño dentro de /var:

find /var -type f -ls | sort -k7 -nr | awk '{print $7 " " $11}' | head -n20

Sólo los archivos de iguales o mayores a cierto tamaño dentro de /var:

find /var -type f -size +1024k -exec ls -lh {} \; | awk '{ print $5 ": " $9 }' | sort -k1 -hr | head -n20
find /var -type f -size +5M -exec ls -lh {} \; | awk '{ print $5 ": " $9 }' | sort -k1 -hr | head -n20

Encontrar los 25 archivos mayores a 1024kB que existen dentro de /var/log:

find /var/log -type f -size +1024k -exec ls -lS {} \+ | head -n25

Directorios iguales o mayores a 1GB:

du -h / | grep ^[0-9.]*G
find / -type d -size +1G

Direcotorios iguales o mayores a 10GB:

du -h / | grep ^[1-9][0-9][0-9.]*G | sort -rn

Direcotorios iguales omayores a 200GB:

du -h / | grep ^[2-9][0-9][0-9][0-9.]*G

Listar los 10 archivos de mayor tamaño (sin directorios), en el directorio actual, de manera recursiva:

find . -printf '%s %p\n' | sort -nr | head -n20

Para restringir el resultado al directorio actual:

find . -maxdepth 1 -printf '%s %p\n'|sort -nr| head -n20

Para listar los 20 archivos-directorio mayores en tamaño:

du -a . | sort -nr | head -n20

Encontrar los 25 directorios de mayor tamaño, de manera recursiva, en el directorio actual:

du -sm */ | sort -rn | head -n25

Encontrar los 25 directorios de mayor tamaño, de menera recursiva, dentro del directorio /var/log/:

du -sm /var/log/* | sort -k1rn | head -n25

El tamaño total de /var/log:

du -sm /var/log
du -sh /var/log

Por favor consulte:

Analizando access.log

Monitoreo simple

tail -f -n 25 access.log

IPs

awk '{print $1}' access.log | sort -u
egrep -o '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]' access.log | sort -u

Top IPs

awk '{print $1}' access.log | sort | uniq -c | sort -nr
egrep -o '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]' access.log | sort | uniq -c | sort -nr
perl -e '$ip{(split)[0]}++ while <>; print map "$_ : $ip{$_}\n", sort {$ip{$b} <=> $ip{$a}} keys %ip' access.log

Top User Agents

awk -F\" '{print $6}' access.log | sort -n | uniq -c | sort -nr

IPs de un Bot

grep 'Googlebot' -i access.log | awk '{print $1}' | sort | uniq

Top URLs

awk -F\" '{print $2}' access.log | sort -n | uniq -c | sort -nr

Status Code

awk '$9 == 500 { print $6 $7}' access.log

grep -Eo "'[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}.* 500' access.log

awk '$9 == 400 { print $6$7 }' access.log