El siguiente ejemplo es un programa awk
completo, el cual imprime el número de ocurrencias de cada palabra en su entrada.
Ilustra la naturaleza asociativa de los arrays de awk usando cadenas como subíndices. También demuestra la construcción `for x in array'. Finalmente, muestra como se puede usar awk en
conjunción con otros programas de utilidades para realizar una tarea útil de
relativa complejidad con un esfuerzo mínimo. Algunas explicaciones aparecen
a continuación del listado del programa.
awk '
# Imprime lista de frecuencias de palabras
{
for (i = 1; i <= NF; i++)
freq[$i]++
}
END {
for (word in freq)
printf "%s\t%d\n", word, freq[word]
}'
La primera cosa a resaltar de este programa es que
tiene dos reglas. La primera regla, debido a que tiene un patrón vacío, se ejecuta
para cada línea de la entrada. Utiliza el mecanismo de acceso a los campos de
awk
(Ver la sección Examinando campos)
para extraer las palabras individuales de la línea, y la variable implícita
NF (Ver la sección 13.
Variables Implícitas (Built-in)) para conocer cuantos campos
están disponibles.
Para cada palabra de entrada, un elementos del array
freq
se incrementa para reflejar que dicha palabra ha aparecido otra vez en el fichero
de entrada.
La segunda regla, debido a que tiene el patrón END, no se ejecuta hasta que se ha acabado de leer
la entrada. Imprime el contenido del array freq
que ha sido construido dentro de la primera acción.
Advierta que este programa presenta distintos problemas
al aplicarlos sobre ficheros de texto reales, ya que no tiene en cuenta los
signos de puntuación.
El lenguaje awk considera los caracteres en mayúsculas y minúsculas como distintos. Por
lo tanto, ‘foo’ y ‘Foo’ no son tratados por este programa como la misma palabra. Esto es indeseable
ya que en el texto normal, las palabras comienzan por mayúsculas si son comienzo
de sentencias, y un analizador de frecuencias no debería ser sensible a eso.
La salida no aparece en un orden útil. Podrías estar
interesado en saber qué palabras aparecen más frecuentemente, o sacar las palabras
y sus respectivas frecuencias en orden alfabético.
La forma de resolver estos problemas es usar otras
utilidades de sistema para procesar la entrada y salida del script de awk. Supón que el script mostrado más arriba se
salva en el fichero ‘frequency.awk’.
Entonces el comando de shell:
tr A-Z a-z < file1 ¦ tr -cd 'a-z\012' \
¦ awk -f frequency.awk \
¦ sort +1 -nr
produce un tabla de las palabras que aparecen en el fichero ‘file1’ en orden de frecuencia de aparición decreciente.
El primer comando tr
en este pipeline transforma todas las letras en mayúsculas del fichero ‘fichero1’
a minúsculas. El segundo comando tr elimina todos los caracteres de la entrada excepto los caracteres en
minúsculas y los caracteres newline. El segundo argumento del segundo tr esta entre comillas para proteger a la barra invertida de que sea interpretada
por la shell. El programa awk
lee los datos de entradas con las transformaciones realizadas previamente por
la ejecución de los comandos tr y produce una tabla de frecuencia de palabras,
la cual no está ordenada.
La salida del script awk
es ahora ordenada por el comando sort
e impresa en el terminal. Las opciones suministradas a sort en este ejemplo especifican la ordenación por
el segundo campo de cada línea de entrada (saltándose un campo), que las claves
de ordenación sean tratadas como cantidades numéricas (de otra forma ‘15’ vendría antes de ‘5’), y que la ordenación se debería hacer en orden descendente (inverso).
Supón que, en un sistema DataWarehouse, tienes que tratar un fichero de texto
gigante (de varios cientos de Megabytes) y te gustaría conocer los valores que te envían
para varios de sus campos que hacen referencia a la clave primaria de otras tablas.
El siguiente ejemplo es un programa de Shell
Script (Korn Shell de Unix) en el que se
utiliza awk para realizar lo que sería una agrupacion en SQL, para saber los valores que
presenta un determinado campo, el número de registros que tiene cada valor y su porcentaje
con respecto al total de registros.
Las consideraciones para este ejemplo son las siguientes:
Una muestra de los registros que presenta este fichero sería la siguiente:
7083211086652975~SIN MATRIC~1999-07-15~9999-12-31~z~008~1086658
704314580149969~M-9870-ID~1998-04-06~9999-12-31~z~005~145806
7078831752339974~M-4921-OV~1997-06-26~1999-04-26~z~001~175239
7078830306279991~M-4730-OH~1994-02-08~1996-03-12~z~001~30633
7078830977159967~553745C~1996-07-03~1998-08-05~z~001~97721
Aquí tienes el programa:
#!/bin/ksh
# #########################################################################
# El formato del fichero o paquete a procesar sera el siguiente:
# campo1~campo2~campo3~campo4~campo5
# !!! DETRAS DEL ULTIMO CAMPO NO IRA EL SEPARADOR !!!
#
# Para quitarle los gusanillos del final de linea si los tuviera habria que
# ejecutar lo siguiente:
#
# sed "1,$ s/\~$//g" fichero_a_tratar > fichero_tratado
# #########################################################################
clear
nawk -F~ ' {
num_total_reg++;
for (i=1;i<=NF;i++) {
# Posibilidad 1: Agrupar y obtener los distintos valores para
# todos y cada uno de los campos
# v_array[i,$i]++;
# Posibilidad 2 (la del ejemplo): Que solo nos saque la agrupacion de unos
# determinados campos. Codigo de bloqueo de la tarjeta
# (campo 5) y marca de la tarjeta (campo 6)
if (i==5 ¦¦ i==6) v_array[i,$i]++;
}
}
END {
for (i in v_array) {
split(i,array_sep,SUBSEP);
print array_sep[1],"~",array_sep[2],"~",v_array[i],"~",(v_array[i]/num_total_reg)*100,"~";
}
} ' tarjetas.txt > /tmp/kk.txt
sort -t~ +0n +2nr /tmp/kk.txt > /tmp/kk2.txt
if [ $? -ne 0 ]
then
echo "Error al ordenar el fichero /tmp/kk.txt"
exit 9
fi
rm /tmp/kk.txt
nawk -F~ '
BEGIN {
num_campo = 0;
num_reg_total = 0;
valor_anterior = "??????";
print " Nombre Campo Valor Num. Regs % ";
print "------------------ ------------------------------ ---------- ---------";
}
{
# Vamos a controlar cuando se cambia de campo para hacer un salto de linea
if (valor_anterior!=$1) {
num_campo++;
valor_anterior=$1;
print "";
}
# Calculo del numero de registro totales del fichero tratado
if (num_campo==1) { num_reg_total += $3; }
# Asociamos el numero de campo con su DESCRIPCION
if ($1==5) $1="cod_bloqueo_trj";
if ($1==6) $1="marca_tarjeta";
# Formateamos el porcentaje para alinearlo por la derecha
porcentaje = sprintf("%3.4f%%",$4);
printf "%18s %30s %10s %9s\n",$1,$2,$3,porcentaje;
}
END {
print "";
print " Numero total de registros procesados: ",num_reg_total;
} ' /tmp/kk2.txt > /tmp/out.txt
cat /tmp/out.txt
rm /tmp/kk2.txt
rm /tmp/out.txt
La salida que generaría la ejecución
de este Shell Script es la siguiente:
Nombre Campo
Valor
Num. Regs
%
------------------ ------------------------------ ---------- ---------
cod_bloqueo_trj
z
8209
96.2481%
cod_bloqueo_trj
41
138
1.6180%
cod_bloqueo_trj
35
76
0.8911%
cod_bloqueo_trj
17
51
0.5980%
cod_bloqueo_trj
43
31
0.3635%
cod_bloqueo_trj
37
24
0.2814%
marca_tarjeta
008
5924
69.4571%
marca_tarjeta
001
2115
24.7977%
marca_tarjeta
007
215
2.5208%
marca_tarjeta
002
150
1.7587%
marca_tarjeta
005
116
1.3601%
marca_tarjeta
004
9
0.1055%
Numero total de registros procesados: 8529
Manual |