Biblioteca Online : ¡La Suscripción ENI por 9,90 € el primer mes!, con el código PRIMER9. Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
  1. Libros
  2. Programación shell en Unix/Linux
  3. El lenguaje de programación awk
Extrait - Programación shell en Unix/Linux ksh, bash, estándar POSIX (con ejercicios corregidos) (5ª edición)
Extractos del libro
Programación shell en Unix/Linux ksh, bash, estándar POSIX (con ejercicios corregidos) (5ª edición) Volver a la página de compra del libro

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

instrucción > "archivo" 

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. 

instrucción >> "archivo" 

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. 

print[f] "..." |"comando" 

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...