El lenguaje de programación awk
Presentación
El lenguaje de programación awk es una utilidad adaptada al tratamiento de archivos de texto. Permite realizar acciones sobre registros de datos incluso estructurados en campos. El nombre "awk" tiene como origen las iniciales de cada uno de sus autores: Aho, Weinberger y Kernighan. Este capítulo presenta las funcionalidades principales de las recientes versiones del lenguaje awk, llamadas nawk (new awk) en varias plataformas Unix. En Linux, el comando awk es un enlace simbólico al intérprete gawk (GNU awk).
1. Sintaxis
awk [-F] '{acción-awk}' [ arch1 ... archn ]
awk [-F] -f script-awk [ arch1 ... archn ]
El comando awk recibe como argumento la lista de archivos que se han de tratar. Ante la ausencia de archivos en la línea de comandos, awk trabaja con los datos que le lleguen por su entrada estándar. Por lo tanto, este comando puede ponerse después de una tubería de comunicaciones.
2. Versión de gawk
La versión de gawk se obtiene utilizando la opción --version.
Ejemplo
$ gawk --version
GNU Awk 4.1.3, API: 1.1
3. Variables especiales
a. Variables predefinidas a partir de la ejecución de awk
La tabla siguiente presenta las principales variables internas del lenguaje awk presentes en memoria desde el primer momento de su ejecución. El valor de estas variables puede modificarse, si se desea, en función de la estructura de datos que se han de tratar.
Nombre de la variable |
Valor por defecto |
Función de la variable |
RS |
Salto de línea (Newline) (\n) |
Record Separator: carácter separador de registros (líneas). |
FS |
Serie de espacios o tabulaciones |
Field Separator: caracteres separadores de campos. |
OFS |
Espacio |
Output Field Separator: separador de campo usado para la visualización. |
ORS |
Salto de línea (Newline) (\n) |
Output Record Separator: carácter separador de registros en la salida. |
ARGV |
- |
Tabla inicializada con los argumentos de la línea de comandos (opciones y nombre del script awk excluidos). |
ARGC |
- |
Número de elementos contenidos en la tabla ARGV. |
ENVIRON |
Variables de entorno exportadas por el shell |
Tabla que contiene las variables de entorno exportadas por el shell. |
SUBSEP |
\034 |
Separador de claves en la simulación de tablas multidimensionales (ver sección Tablas). |
Por defecto, un registro se corresponde...
Operadores
La tabla siguiente agrupa los operadores disponibles en este lenguaje.
Operador |
Aridad |
Significado |
Operadores aritméticos |
||
+ |
Binario |
Suma. |
- |
Binario |
Resta. |
* |
Binario |
Multiplicación. |
/ |
Binario |
División. |
% |
Binario |
Módulo. |
^ |
Binario |
Exponenciación. |
++ |
Unario |
Incremento de una variable de una unidad. |
-- |
Unario |
Decremento de una variable de una unidad. |
+= |
Binario |
x+=y es equivalente a x=x+y. |
-= |
Binario |
x-=y es equivalente a x=x-y. |
*= |
Binario |
x*=y es equivalente a x=x*y. |
/= |
Binario |
x/=y es equivalente a x=x/y. |
%= |
Binario |
x%=y es equivalente a x=x%y. |
^= |
Binario |
xˆ=y es equivalente a x=xˆy. |
Operadores de verificaciones |
||
< |
Binario |
Menor. |
> |
Binario |
Mayor. |
<= |
Binario |
Menor o igual. |
>= |
Binario |
Mayor o igual. |
== |
Binario |
Verificación de igualdad. |
!= |
Binario |
Verificación de desigualdad. |
~ |
Binario |
Correspondencia con una expresión regular. |
!~ |
Binario |
No correspondencia con una expresión regular. |
Operadores lógicos |
||
! |
Unario |
Negación. |
&& |
Binario |
Y lógica. |
|| |
Binario |
O lógica. |
Varios |
||
= |
Binario |
Asignación. |
e1 ? e2: e3 |
Ternario |
La expresión global vale e2 si e1 es veradero, e3 en caso contrario. |
e1 e2 (operador espacio) |
Binario |
Concatenación de e1 y e2. |
Ejemplo de concatenación
Concatenación (operador espacio) de las variables nombre y apellido, en la variable nombreApellido:
$ awk 'BEGIN { apellido="Durand" ; nombre="Michel"...
La función printf
awk ofrece la función integrada printf, similar a la del lenguaje C. Permite formatear los textos de salida.
printf ("cadena",expr1,expr2,...,exprn)
cadena representa la cadena que se mostrará por pantalla. Puede contener formatos que serán sustituidos por el valor de las expresiones citadas a continuación. Tiene que haber tantos formatos como expresiones.
Ejemplos de formatos comúnmente utilizados
%20s |
Visualización de una cadena (string) de 20 posiciones (alineada a la derecha por defecto). |
%-20s |
Visualización de una cadena (string) de 20 posiciones con alineación a la izquierda. |
%3d |
Visualización de un entero (decimal) de 3 posiciones (alineación a la derecha). |
%03d |
Visualización de un entero (decimal) de 3 posiciones (alineación a la derecha) completado con 0 a la izquierda. |
%-3d |
Visualización de un entero (decimal) de 3 posiciones (alineación a la izquierda). |
%+3d |
Visualización de un entero (decimal) de 3 posiciones (alineación a la derecha) con escritura sistemática del signo (un número negativo siempre mostrará su signo). |
%10.2f |
Visualización de un número en coma flotante de 10 posiciones, 2 de las cuales son decimales. |
%+010.2f |
Visualización de un número en coma flotante de 10 posiciones, 2 de las cuales son decimales; alineación a la derecha, con escritura sistemática... |
Redirecciones
Es posible redirigir las salidas del script hacia un archivo o hacia un comando del sistema.
Sintaxis
|
En la primera llamada, apertura en modo "sobrescritura", seguido de escritura. Las escrituras siguientes se realizan a continuación de la línea anterior. La expresión "archivo" valdrá "/dev/stderr" para escribir en la salida de error estándar. |
|
En la primera llamada, apertura en modo "adición", seguido de escritura. Las escrituras siguientes se realizan a continuación de la línea anterior. La expresión "archivo" valdrá "/dev/stderr" para escribir en la salida de error estándar. |
|
El resultado de la instrucción print se transmite a la entrada estándar del comando mediante una tubería. |
Primer ejemplo
Apertura en modo sobrescritura:
$ nl redireccion1.awk
1 BEGIN {
2 nombrearch = "/tmp/arch.txt"
3 print "Línea 1" > nombrearch
4 print "Línea 2" > nombrearch
...
Lectura de la línea siguiente: next
La instrucción next interrumpe el tratamiento de la línea actual y desencadena la lectura de la línea siguiente, en la que se aplicará el tratamiento integral.
Ejemplo
El script region.awk muestra el archivo tel3.txt, añadiendo un campo, concatenando el valor PB delante del número de teléfono de los clientes localizados en la zona horaria de la península y C delante del número de teléfono de los clientes localizados en la zona horaria de las islas Canarias.
$ nl region.awk
1 BEGIN {
2 FS="|"
3 }
4 $3 ~ /^(35|38)/ {
5 printf ("%s|%s|%s|%s|PB|%s\n",$1,$2,$3,$4,$5)
6 # Salto al registro siguiente
7 next
8 }
9 {
10 printf ("%s|%s|%s|%s|C|%s\n",$1,$2,$3,$4,$5)
11 }
Ejecución:
$ awk -f region.awk tel3.txt
Méndez Roca, Gisela|calle Ruiseñor|28023|Madrid|PB|915.351.478
Ruiz del Castillo, Marcos|calle...
Estructuras de control
awk ofrece estructuras de control que normalmente se encuentran en lenguajes de programación. La sintaxis se ha heredado del lenguaje C.
1. if
La parte else es opcional.
Sintaxis
if (condición) {
instrucción
...
}
else {
instrucción ...
}
Cuando solo hay una instrucción, las llaves son opcionales:
if (condición)
instrucción
else
instrucción
2. switch
gawk |
La estructura de control switch (equivalente en shell a la estructura case) permite a su vez realizar pruebas.
La estructura de control switch se encuentra disponible como estándar a partir de la versión 4 de gawk (en las versiones superiores a 3.1.3 e inferiores a 4, switch está disponible si gawk se compila con la opción --enable-switch).
Sintaxis
switch (expresion) {
case valor|expresion-regular:
instrucción
instrucción
...
break
case valor|expresion-regular:
instrucción
instrucción
... ...
Finalizar un script
La instrucción exit permite en todo momento finalizar un script devolviendo el estado al sistema.
Ejemplo
{
if ( NF < 3) exit 1; # Fin del script con estado falso
. . .
. . .
}
END{
exit 0 # Fin del script con estado verdadero
}
Tablas
Los elementos de una tabla pueden indexarse por un número o una cadena de caracteres. Este índice se ve siempre como una cadena de caracteres en el lenguaje awk, todas las tablas son asociativas. Sin embargo distinguiremos los dos casos en la figura.
1. Tablas indexadas con un número
El índice de partida es elegido por el programador.
Ejemplo
Este script inicializa un elemento de la tabla en cada nuevo registro tratado. El archivo tratado es tel3.txt. Cada elemento representa el nombre de un cliente. Esta tabla se indexa a partir de 1:
$ nl tab.awk
1 # Sección BEGIN
2 BEGIN {
3 FS="|"
4 }
5 # Tabla que almacena los nombres de los clientes
6 {
7 cliente[NR]=$1
8 }
9 # Sección END
10 END {
11 # Visualización de la tabla
12 for (indice=1; indice <= NR; indice++)
13 printf("Cliente no %4d => %-20s\n",indice, cliente[indice]);
14 }
$
Resultado de la ejecución:
$ awk -f tab.awk tel3.txt
Cliente no 1 => Méndez Roca, Gisela
Cliente no 2 => Ruiz del Castillo, Marcos
Cliente no 3 => Hernández Darín, Alberto
Cliente no 4 => Gómez Bádenas, Josefina
Cliente no 5 => Martínez Parra, Marta
Cliente no 6 => Expósito Heredia, Pedro
$
2. Tablas indexadas por una cadena de caracteres
Cuando el índice es una cadena de caracteres, se prefiere el término « clave » al término "índice".
Ejemplo
El archivo ventas.txt contiene información acerca de las ventas de una sociedad. La información está clasificada por poblaciones. A001:100 representa el código de un artículo...
Tablas multidimensionales
1. Simulación de tablas multidimensionales
Con la excepción de gawk a partir de la versión 4, no existen verdaderas tablas multidimensionales. Sin embargo, existe un mecanismo que permite simular el funcionamiento de una tabla multidimensional.
Ejemplo
$ nl tab2d.awk
1 BEGIN {
2 # Clave única
3 tab[0,"nombre"] = "Paredes"
4 tab[0,"cp"] = "28030"
5 tab[1,"nombre"] = "Perez"
6 tab[1,"cp"] = "08300"
7
8 for (i=0; i<=1; i++) {
9 print "Indice " i " : "
10 print "Nombre : " tab[i,"nombre"]
11 print "CP : " tab[i,"cp"]
12 print "------------"
13 }
14
15 for (clave in tab) {
16 print "Clave : --" clave "-- Valor => " tab[clave]
17 }
18 }
En realidad, la clave [0,"nombre"] se almacena...
Los argumentos de la línea de comandos
Las variables ARGV y ARGC
awk proporciona un mecanismo que permite pasar argumentos a un script en el momento de su llamada. Las variables ARGC y ARGV se inicializan por awk y permiten tratar los valores pasados en la línea de comandos. ARGV es una tabla que contiene los argumentos recibidos, salvo las opciones awk de la línea de comandos (más adelante -f arg.awk). ARGV[0] representa el nombre del comando (por lo tanto, awk). Los valores contenidos en los siguientes ítems de la tabla ARGV se van a considerar como los nombres de archivos que las secciones intermedias van a procesar. ARGC contiene el número de argumentos recibidos. ARGV y ARGC están disponibles en todas las secciones del script.
Ejemplo
$ nl arg.awk
1 #! /bin/awk
2 BEGIN{
3 print "ARGC = " , ARGC
4 for (i=0;i<ARGC;i++) {
5 printf("ARGV[%d] = %s\n",i, ARGV[i])
6 }
7 }
$
$ awk -f arg.awk agenda.txt tel3.txt
ARGC = 3
ARGV[0] = awk
ARGV[1] = agenda.txt
ARGV[2] = tel3.txt
Enviar variables...
Funciones integradas
El lenguaje awk dispone de funciones integradas.
1. Funciones que trabajan con cadenas
Función |
Rol |
gsub(er,reemp,[cad]) |
Remplaza en la cadena "cad" cada ocurrencia correspondiente a la expresión regular "er" por la cadena "reemp". Devuelve el número de sustituciones. Por defecto, "cad" vale $0. |
index (cad1,cad2) |
Devuelve la posición de la subcadena "cad2" en la cadena "cad1". |
length(cad) |
Devuelve la longitud de la cadena "cad". |
match(cad,er) |
Devuelve la posición en la cadena "cad" de la primera ocurrencia de la expresión regular "er". |
split(cad,tab,sep) |
Inicializa la tabla "tab" con los campos de la cadena "cad". "sep" representa el separador de campos. Devuelve el número de campos. |
sprintf(fmt,e1,...,en) |
Idéntico a printf, pero devuelve la cadena formateada. |
sub(er,reemp,[cad]) |
Remplaza en la cadena "cad" la primera ocurrencia correspondiente a la expresión regular "er" por la cadena "reemp". Devuelve el número de sustituciones. Por defecto, "cad" vale $0. |
substr(cad,pos,lg) |
Devuelve la subcadena de "cad" que comienza en la posición "pos" y de longitud "lg". |
tolower(cad) |
Devuelve el valor de la cadena "cad" convertida en minúsculas. |
toupper(cad) |
Devuelve el valor de la cadena "cad" convertida en mayúsculas. |
2. Funciones matemáticas
Función |
Rol |
cos(x) |
Devuelve el coseno de x |
exp(x) |
Devuelve e elevado a x |
int(x) |
Devuelve el valor entero de x |
log(x) |
Devuelve el logaritmo natural de x |
sin(x) |
Devuelve el seno de x |
sqrt(x) |
Devuelve la raíz cuadrada de x |
atan2(x,y) |
Devuelve el arcotangente de y/x |
rand() |
Devuelve un número aleatorio tal que 0<=y< 1 |
srand(x) |
Inicializa la base de cálculo para rand() en función del valor x. Devuelve la base antigua. |
3. Funciones vinculadas a las tablas
Estas funciones se presentan en la sección Tablas.
4. Otras funciones
a. La función getline
La función getline permite:
-
Leer la línea siguiente del flujo de datos sin subir al comienzo del tratamiento (no como sucede con next).
-
Leer una línea desde un archivo, de la entrada estándar o de una tubería.
Valor de retorno:
-
1 en caso de éxito.
-
0 si está...
Funciones de usuario
Sintaxis
Una función puede tener de 0 a n argumentos y devolver 1 valor explícito. Las funciones pueden definirse antes o después de su llamada.
Definición de una función:
function nombre_funcion (param1, param2 ..., paramn) {
return valor
}
Los parámetros (param1, param2 ..., paramn) son variables locales. Cualquier otra variable definida en la función es global.
Llamada a una función:
valor_devuelto=nombre_funcion(val1, val2, ..., valn)
No tiene que haber espacios entre el nombre de la función y el paréntesis de apertura.
Ejemplo
El script region2.awk muestra el archivo tel3.txt, añadiendo un campo delante del número de teléfono de los clientes: "PB" para los clientes localizados en la Península y Baleares y "C" para los clientes localizados en las dos provincias de las islas Canarias. La función getRegion() recibe como parámetros el código postal y devuelve la cadena que representa la región.
$ nl region2.awk
1 # Función que determina el valor de la región
2 function getRegion (cp) {
3 # región canaria
4 if ( cp ~ /^(3[58])/ )
5 ...
Inclusión de archivos
gawk >= 4 |
El lenguaje gawk ofrece, a partir de su versión 4, la posibilidad de incluir en el código fuente el contenido de otros archivos, mediante la directiva @include. La variable de entorno AWKPATH puede inicializarse con el nombre de los directorios donde gawk deberá buscar los archivos a incluir (mismo principio que la variable PATH Unix). Si AWKPATH está inicializada, esta también deberá contener la ubicación del script principal.
Ejemplo de inclusión
El archivo a incluir:
$ nl mostrar.inc.gawk
1 function mostrar (mensaje) { print mensaje }
El programa principal:
$ nl include.gawk
1 @include "mostrar.inc.gawk"
2 BEGIN { mostrar ("Hello") }
$ gawk -f include.gawk
Hello
Ejemplo de utilización de AWKPATH
El programa principal:
/home/cristina/awk/AWKPATH/include.gawk
incluído:
/home/cristina/awk/AWKPATH/includes/mostrar.inc.gawk
Definición de la variable AWKPATH que debe contener la ubicación del programa principal y de los archivos a incluir:
$ AWKPATH=/home/cristina/awk/AWKPATH:/home/cristina/awk/AWKPATH/includes
$ export AWKPATH
$ gawk -f include.gawk
Hello
$
La variable y su exportación...
Ejercicios
Los archivos proporcionados para los ejercicios están disponibles en la carpeta dedicada al capítulo, en el directorio Ejercicios/Archivos.
1. awk en línea de comandos
a. Ejercicio 1: awk y otros filtros
Comandos filtro útiles: awk, grep (ver capítulo Los comandos filtro), sed (ver capítulo El comando sed). Otro comando útil: file.
Muestre los nombres de los archivos de texto del directorio /etc.
Ejemplo de resultado
adjtime
aliases
asound.conf
auto.master
auto.misc
. . .
b. Ejercicio 2: criterios de selección
1. |
En su directorio actual, muestre las características de los archivos cuyo nombre comience con un punto (solo estos). |
Ejemplo de resultado
drwxr-xr-x. 24 cristina cristina 4096 3 feb 12:26 .
drwxr-xr-x. 11 root root 4096 27 ene 14:06 ..
-rw-------. 1 cristina cristina 14752 22 ene. 12:40 .bash_history
2. |
En su directorio actual, muestre los nombres de los archivos que comienzan con un punto, salvo "." y "..". |
Ejemplo de resultado
.bash_history
.bash_logout
.bash_profile
.bashrc
c. Ejercicio 3: criterios de selección, visualización de campos, secciones BEGIN y END
A partir del archivo php.ini proporcionado:
1. |
Muestre las líneas que no comiencen por ";" y que terminen en On u Off. |
Ejemplo de resultado
engine = On
short_open_tag = Off
asp_tags = Off
zlib.output_compression = Off
implicit_flush = Off
2. |
Mejore la visualización. |
Ejemplo de resultado
engine On
short_open_tag Off
asp_tags Off
zlib.output_compression Off
implicit_flush Off
zend.enable_gc On
. . .
3. |
Recupere el comando anterior y muestre el número de directivas encontradas... |