AWK Paso A Paso
AWK Paso A Paso
AWK Paso A Paso
Introduccin
Me pide Bernat que le ayude a hacer una faena rpida. Es tan fcil como procesar
automticamente el contenido de una hoja de clculo. En ella, cada fila contiene datos
de un alumno de la Universidad. Se pretende extraer de la hoja el nombre de usuario y
la contrasea de cada estudiante, y autorizar su acceso al contenido proporcionado por
un servidor web.
Estoy en franca desventaja debido a mis problemas cutneos (padezco una urticaria que
se manifiesta cada vez que me acerco a un ordenador con Windows) Un tercer
investigador de la Compaa para la que trabajamos se abalanza sobre el problema,
debidamente pertrechado con sus cientos de gigabytes, gigaciclos y gigaeuros
dudosamente gastados en visualestudios variados.
Cuando nuestro jovial amigo ha pasado un buen rato buceando por las profundidades
delAPI de Excel, extraos problemas relacionados con el flamenco y las Fuerzas
Armadas --aparece en la pantalla algo relacionado con OLE y el General Failure-- le
apartan de la cuestin principal. Yo, por mi parte, decido volver sobre el tema
realizando una aproximacin ms humilde.
Por qu no awk? Al fin y al cabo, esta herramienta fue diseada en los albores
delUNIX precisamente para resolver con poco esfuerzo situaciones en las que se desea
leer un archivo lnea a lnea, y tomar decisiones en funcin del contenido particular de
cada registro.
Los adictos a las mil y una variedades del clic (single click, double click, right button
click, close your eyes and clap your hands click, etc.) encontrarn seguramente alguna
ocupacin alternativa a la lectura de este artculo. Solamente los locos de la pantalla
negra podrn seguir leyendo sin experimentar unas ganas irresistibles de vomitar. El
que avisa no es traidor.
Supongamos que el lector selecto --es decir, el que sigue leyendo a pesar de la enorme
oferta televisiva del momento-- dispone de un ordenador con su correspondiente
sistema operativo y de lo necesario para ejecutar awk. (A decir
verdad, Windows tambin puede servir en este caso, ya que existe una versin
de awk para msdos, win16 , win32, etc. Con un pequeo esfuerzo adicional los usuarios
de este tipo de me-reservo-el-calificativo pueden seguir este artculo a pesar de no
tener un sistema decente que llevarse al procesador)
Prerrequisitos
Se presupondr en adelante que el lector dispone de alguna variedad
de UNIX(permtaseme un inciso para reconocer el mrito del pedante annimo que
acu la expresin sabores de UNIX)
No se requiere el uso de una shell concreta. En los ejemplos se utiliza bash por ser
capaz de interpretar caracteres de control escritos con la sintaxis de escape con barra
invertida.
$ awk
Usage: awk [-f programfile | 'program'] [-F fieldsep] [-v var=value] [files]
Ms adelante se mostrar cmo awk procesa cada registro como una sucesin de
campos separados por un carcter determinado que indica dnde termina un campo y
empieza el siguiente. La situacin ms habitual es aquella en la cual cada registro
contiene una frase y los campos son las palabras de la frase. El carcter delimitador es
el espacio que separa cada palabra de la siguiente. Mediante el parmetro F (ahora
mayscula) se indica a awk qu carcter debe considerar como separador de campos.
Tambin se mostrar la capacidad para usar variables dentro del guin, igual que en
otros lenguajes de programacin. Mediante el parmetro v se puede asignar un valor a
una variable desde la lnea de mandatos:
El resto de los parmetros son los nombres de los archivos de datos entrantes. El
proceso se realiza accediendo a los archivos en el orden especificado. Toma el conjunto
de los archivos como si fueran uno solo. Awk espera recibir por la entrada estndar
(stdin) los datos que debe procesar. Por ejemplo:
Suponga el lector que se desea actuar sobre un archivo de texto como el que se
muestra a continuacin. Contiene cuatro lneas de texto terminadas por el carcter de
salto de lnea \n, que no se muestra en la pantalla, salvo por el efecto de escribir en la
lnea siguiente:
$ cat ejemplo.txt
La bella y graciosa moza
marchse a lavar la ropa
la frot sobre una piedra
y la colg de un abedul.
$ _
expresin { accin }
Esta plantilla contiene una expresin trivial (1), cuya evaluacin resulta siempre
positiva. En ausencia de una accin asociada explcitamente, awk asume la accin por
omisin, que consiste en mostrar la lnea completa en stdout. Se puede aadir una
accin algo ms sofisticada entre llaves:
Como puede observarse, la accin print se realiza una vez por cada lnea que se obtiene
de stdin. Para emplear ms de una plantilla se emplea la construccin expresin
{ accin } repetidas veces, como en el siguiente ejemplo:
Campos de entrada
Para utilizar una expresin que no sea trivial se debe introducir el concepto de campo.
Se considerar cada registro del archivo de entrada como una sucesin de campos
delimitados por un carcter dado. Este carcter es, por omisin, el espacio. Como se ha
visto anteriormente, se puede indicar a awk que considere otro carcter como separador
de campos mediante la opcin F (mayscula) en la lnea de mandatos.
Se hace referencia al contenido de cada campo usando el carcter $ (dlar) seguido
del ordinal del campo: $1, $2, $3, etc. Con la notacin $0 se hace referencia a la lnea
entera.
Se puede forzar el proceso del registro carcter a carcter dejando la variable
separador de campos FS sin contenido. De este modo, en $1 se tendr el primer
carcter de la lnea, en $2 el segundo, etc. Incluso en este caso, el campo $0 contendr
la lnea completa.
Como quiera que se est anticipando en estos ejemplos el uso de la instruccin "print",
se muestra a continuacin el guin anterior con las modificaciones necesarias para
mostrar en stdout el contenido de la primera y la segunda palabra de cada lnea:
$ cat ejemplo.txt
registro." }'
Primera
Primera
Primera
Primera
$ _
y
y
y
y
segunda
segunda
segunda
segunda
| awk '1 { print Primera y segunda palabras ===>, $1, $2, "<=== final del
palabras
palabras
palabras
palabras
===>
===>
===>
===>
Variables
Tambin es necesario en este punto citar la existencia de variables predefinidas en el
lenguaje. El concepto tradicional de variable (almacn de datos en memoria,
un
apple
ORS (Output RS): Contiene el carcter que awk escribir al final de cada registro. Es
\n por omisin. Obsrvese que en esta modificacin del ejemplo anterior la entrada
contiene DOS registros, dada la aparicin del salto de lnea en el centro de la cadena de
un
apple
caro
elegante
Operadores
Son aplicables los operadores comunes en C para realizar operaciones aritmticas sobre
variables con valores numricos, y para concatenar los valores contenidos en variables
con valores alfanumricos. As, se tiene:
Suma
Resta
Multiplicacin
Divisin
Resto
Exponenciacin
Esp
Concatenacin
Negacin
A ++
A --
A += N
A -= N
A *= N
Multiplicar por N
A /= N
Dividir por N
A %= N
A ^= N
>
Mayor que
>=
<
Menor que
<=
==
Igual a
!=
Distinto de
?:
Awk en accin
Para recapitular lo visto hasta ahora, se realizar a continuacin algn proceso sencillo
con el archivo de ejemplo mostrado en prrafos anteriores. El archivo entrante contiene
un nmero tericamente no determinado de registros, terminados por el carcter
contenido en RS. El proceso comienza leyendo el primer registro, aplicando a ste todas
las plantillas existentes con el valor de la variable NR igual a 1. Se reitera el proceso
para cada registro, incrementando en una unidad el valor de NR cada vez.
Cada registro est compuesto por NF campos, delimitados por las apariciones del
carcter FS en el mismo. El ordinal de cada campo se designa, como se indic arriba,
con $1, $2, $3 y as hasta $NF.
Con campos, variables y operadores se puede escribir una plantilla algo ms til que las
anteriores:
$ awk '$5 == moza { print La linea, NR, "contiene MOZA en el quinto campo; }' ejemplo.txt
La lnea 1 contiene MOZA en el quinto campo
$ _
$ cat proceso.awk
{ print "Procesando la lnea", NR }
NF > 1 { print La lnea tiene ms de una palabra }
$1 == Linux {print La primera palabra es Linux}
$ _
La primera plantilla siempre se ejecuta por no tener expresin delante de las llaves. La
segunda solamente se activa para las lneas que tienen ms de una palabra. La tercera
es aplicable si la lnea comienza con la palabra Linux.
$ cat suma.awk
BEGIN { total = 0 }
{ total += $1 }
END { print El total es, total }
Para ejecutarlo, se puede usar lo siguiente (obsrvese que la entrada tiene cuatro
registros):
Instrucciones de control
Algunas instrucciones del lenguaje C estn presentes en awk de manera simplificada. Se
dispone de la construccin condicional if/else , que se codifica de la manera habitual:
{
print Procesando registro, NR;
if( $1 > 0 )
{ print "positivo";
print "El primer campo tiene:", $1;
}
else
{
print "negativo";
}
donde la primera expresin suele poner valor inicial al ndice. En la segunda se controla
hasta qu momento la ejecucin debe continuar. La tercera marca el nivel de
incremento de la variable ndice.
{
for( i = 0; i < 10; i ++ )
{
j = 2 * i;
print i, j;
}
En su segunda forma, la construccin for recorre los elementos de una matriz (array).
Dado el carcter avanzado de este concepto, quedar para un segundo artculo su
manejo.
La construccin do/while se emplea en la forma habitual. En el caso actual se realiza
un sencillo contador de cero a diez:
Funciones
Las funciones predefinidas del lenguaje responden al concepto habitual en otros
lenguajes de programacin. La llamada a una funcin se resuelve en tiempo de
ejecucin. Cuando se evala una expresin, se realiza la sustitucin de la llamada a la
funcin por el valor que sta devuelve. Por ejemplo:
$ awk 'BEGIN { print El seno de 30 grados es, sin( 3.141592653589 / 6 ); }' /dev/null
El seno de 30 grados es 0.5
$ _
Se dispone de las funciones matemticas y trigonomtricas exp, log, sqrt, sin, cos y
atan2. Adems, son de mucha utilidad las siguientes:
length( )
rand( )
srand( )
int( )
substr(s,m,n)
index(s,t)
match(s,r)
split(s,a,fs)
sub(r,t,s)
gsub(r,t,s)
sprintf(f,e,e)
system(cmd)
tolower(s)
toupper(s)
getline
La solucin definitiva
El lector avezado debera estar, a estas alturas, preparado para afrontar el desafo del
principio del artculo. Con los mimbres mostrados en las pginas anteriores se tratar de
hacer el cesto deseado. Ser suficiente con que el guin awk resultante sea capaz de
construir un shell script que contenga los mandatos necesarios para autorizar el acceso
de los alumnos al servidor web.
$ cat alumnos.txt
Sebastin Palomo Linares
spl18901
jalisco
Pedro Prez Pino
ppp0
hatalue
John Jensen
jotajota
whatafew
Mara del Carmen Garca Feo
mc98819
uglymom
$ _
En la salida se desea generar un shell script con el comentario que indica el nombre de
la shell a emplear (#!/bin/bash, por ejemplo) en la primera lnea, y con una lnea
como /usr/bin/htpasswd b usuario contrasea" por cada alumno.
Para poner una pizca de dificultad al problema, pondremos de manifiesto un hecho que
seguramente no extraar al lector; los nombres no tienen un nmero fijo de palabras,
y no se garantiza que todos los alumnos estn expresados con los dos apellidos. As que
el planteamiento simplista que hara un anglosajn no muy despierto, consistente en
asociar $1 con el nombre, $2 con el apellido, $3 con la cuenta y $4 con la contrasea,
no conduce al resultado correcto:
b
b
b
b
Linares spl18901
Pino ppp0
jotajota whatafew
Carmen Garca
Otra dificultad que puede presentarse viene dada por la eventual inexistencia de la base
de datos de autorizaciones del servidor web en el momento de la ejecucin del script. A
los efectos de la programacin, implica aadir la opcin -c a la primera llamada al
mandato htpasswd dentro del script que se genera.
La aproximacin correcta al problema consiste en obtener el nmero de campos
(palabras) del registro (lnea), y seleccionar la penltima y la ltima. Afortunadamente,
la variable NF contiene en cada momento el nmero de campos de la lnea que se est
procesando. En este ejemplo puede verse cmo se hace uso de esta capacidad:
Pero, lo que es realmente maravilloso en este caso es que podemos acceder al valor del
campo que se encuentra en el ltimo lugar con tan solo $NF en lugar de NF:
:
:
:
:
jalisco
hatalue
whatafew
uglymom
Para dar los toques finales con ms comodidad se escribira el guin en un archivo
llamado altaweb.awk :
$ cat altaweb.awk
BEGIN { print #!/bin/bash }
{
print /usr/bin/htpasswd,
( NR == 1 ? -c -b : -b ),
$(NF 1), $NF
}
END { print # Shell script generado OK. }
$ _
La ejecucin del proceso se realiza, como antes, con el mandato que se muestra a
continuacin:
Solamente resta verificar el contenido del shell script y ejecutarlo. Cuando se tenga
confianza en el resultado se puede obviar el paso intermedio y ejecutar directamente:
Conclusin
Awk es un poderoso intrprete, que permite realizar tareas con archivos de tipo
secuencial con una elevada efectividad. La eficacia de su uso en procesos automatizados
es innegable, a la vista de que mi jovial colaborador ha desistido, y se dispone a
reinstalar Windows. Sin embargo, disfrutar de lo lindo con su salvapantallas de
tiburones simultaneado con apariciones espordicas de una misteriosa pantalla azul en
la que se glosan las maravillas de la Excepcin 0E Hasta ms ver.
Bibliografa
1. man awk
2. A.V. Aho, P. J. Weinberger y B. W. Kernighan, The AWK programming language,
Addison-Wesley, 1988. ISBN 0-201-07981-X.
3. Pgina de Brian Kernighan en Bell Labs, http://www.cs.belllabs.com/who/bwk/index.html
http://www.linux-es.org/node/31
http://www.alcancelibre.org/staticpages/index.php/introduccion-awk