por Javier Palacios Bermejo Sobre el Author:
Está realizando su tesis doctoral en Astronomía en una universidad
española, donde está encargado de la administración
del
Contenidos: |
Resumen:
Este artículo pretende ser una breve introducción al viejo comando/programa
de unix awk
. Aunque no tan popular como el
La idea de escribir este texto surgió por la lectura de un par de
artículos aparecidos en LinuxFocus y escritos por Guido Socher.
Uno de ellos versaba sobre
find y comandos
relacionados y
me hizo ver que al parecer no era el único que usaba todavía
la línea de comandos, en lugar de bonitos GUI que consiguen que no sepas
como se hacen las cosas (que es el camino por el que tiró Windows hace
mucho). El otro de los artículos trataba sobre
expresiones regulares
que, aunque apenas mencionadas en este artículo, resulta muy conveniente
conocer para sacar el mayor partido posible a awk
y algún otro de los
comandos sobre los que pensaba hablar inicialmente en este artículo
(sed
y grep
principalmente).
algunos otros comandos.
La pregunta clave es si es realmente útil este comando, y la respuesta
es que sí. Le puede resultar útil a un usuario para procesar ficheros
de texto, reformatearlos, etc... Para un administrador, awk es, simplemente,
una utilidad casi imprescindible.
Basta con pasear por /var/yp/Makefile
, para darse cuenta
de ello.
awk
Supe de su existencia hace tanto que no lo recuerdo bien. Fue cuando un
compañero tenía que trabajar con unos output impresionantes en un pequeño
Cray y estuvo investigando muchas posibilidades de clasificación. La página
man del awk
en el Cray era de lo más exigua, pero el
decía que parecía
muy bueno para esa tarea aunque no había forma de hincarle el diente.
Mucho tiempo después, se volvió a cruzar en mi vida, mediante una
especie de comentario casual (otro sitio, otro compañero), que lo usaba
para extraer la primera columna de una tabla:
awk '{print $1}' fichero
|
Una vez aprendida la lección extrayendo una columna ya podemos
hacer algunas cosillas como añadir una extensión a una serie de ficheros
con secuencias como
ls -1 pattern | awk '{print "mv "$1" "$1".nuevo"}' | sh
|
Y más aún ...
ls -1 *viejo* | awk '{print "mv "$1" "$1}' | sed s/viejo/nuevo/2 | sh
ls -l * | grep -v drwx | awk '{print "rm "$9}' | sh
ls -l | grep '^d' | awk '{print "rm -r "$9}' | sh
ls -p | grep /$ | awk '{print "rm -r "$1}' | sh
Feedback de los lectores:
Como Dan Debertin me hizo notar, algunos
de los ejemplos anteriores se pueden realizar sin usar el comando
grep, solo con las capacidades de matcheodel awk
que se mencionan unas líneas más abajo.
ls -l *|grep -v drwx|awk '{print "rm "$9}'|sh sería mas ilustrativo de la potencia de AWK en la forma: ls -l|awk '$1!~/^drwx/{print $9}'|xargs rm también, ls -l|grep '^d'|awk '{print "rm -r "$9}'|sh podría escribirse como ls -l|awk '$1~/^d.*x/{print $9}'|xargs rm -r Uso constantemente la siguiente línea para matar procesos: (digamos que el proceso se llama 'sleep') ps -ef|awk '$1~/'$LOGNAME'/&&$8~/sleep/&&$8!~/awk/{print $2}'|xargs kill -9 (hay que ajustarlo para la forma que adopte el comando ps en el sistema en que trabajes. En algunas ocasiones será ps -aux, el número de campos variará, etc.) Básicamente es "Si el dueño del proceso ($1) soy yo, y si se llama ($8) "sleep", y no se llama "awk" (en ese caso el comando awk se mataría a sí mismo), enviar el PID correspondiente ($2) al comando kill -9.". ¡Y sin usar grep! |
Cuando, por ejemplo, se repiten los mismos cálculos una y otra vez,
estas herramientas resultan una gran ayuda. Y, además, es mucho más
divertido escribir un programa de awk
que repetir manualmente
lo mismo veinte veces.
Aunque nos referimos a él con ese nombre, el awk
no es en
realidad un comando, de igual forma que el gcc tampoco lo es. Awk es en
realidad un lenguaje de programación, con una sintaxis con aspectos
similares al C, y cuyo intérprete se invocacon la instrucción
awk
.
En cuanto a la sintaxis del comando, casi todo está dicho ya:
# gawk --help Usage: gawk [POSIX or GNU style options] -f progfile [--] file ... gawk [POSIX or GNU style options] [--] 'program' file ... POSIX options: GNU long options: -f progfile --file=progfile -F fs --field-separator=fs -v var=val --assign=var=val -m[fr] val -W compat --compat -W copyleft --copyleft -W copyright --copyright -W help --help -W lint --lint -W lint-old --lint-old -W posix --posix -W re-interval --re-interval -W source=program-text --source=program-text -W traditional --traditional -W usage --usage -W version --version Report bugs to bug-gnu-utils@prep.ai.mit.edu, with a Cc: to arnold@gnu.ai.mit.eduBaste destacar que, además de incluir los programas entre comillas sencillas (') en la línea de comandos, se pueden escribir en un fichero que invocamos con la opción
-f
, y que definiendo variables en la
línea de comandos -v var=
Awk es, básicamente, un lenguaje orientado al manejo de tablas, en el sentido de información susceptible de clasificarse en forma de campos y registros, al estilo de las bases de datos más tradicionales. Con la ventaja de que la definición del registro (e incluso del campo) es sumamente flexible.
Pero awk
es mucho más potente. Está pensado para trabajar con registros
de una línea, pero esa necesidad se puede relajar. Para profundizar un
poco en algunos aspectos, vamos a echar un vistazo a algunos ejemplos ilustrativos
(y reales).
awk
), no resulta
demasiado difícil, aunque puede hacerse un poco tedioso:
BEGIN { printf "preambulo LaTeX" printf "\\begin{tabular}" printf "{|c|c|...|c|}" } |
{ printf $1" & " printf $2" & " . . . printf $n" \\\\ " printf "\\hline" } |
END { print "\\end{document}" } |
awk
para
trocearlo. Obviamente, para ello tuve que aprovechar ciertas características
del output.
|
( $1 == "====>" ) { NomObj = $2 TotObj = $4 if ( TotObj > 0 ) { FS = "|" for ( cont=0 ; cont<TotObj ; cont++ ) { getline print $2 $4 $5 $3 >> NomObj } FS = " " } } |
NOTA: Como en realidad no daba el nombre del objeto, era un poco más complicado, pero pretende ser un ejemplo ilustrativo. |
BEGIN { BEGIN_MSG = "From" BEGIN_BDY = "Precedence:" MAIN_KEY = "Subject:" VALIDATION = "[RESUMEN MENSUAL]" HEAD = "NO"; BODY = "NO"; PRINT="NO" OUT_FILE = "Resumenes_Mensuales" } { if ( $1 == BEGIN_MSG ) { HEAD = "YES"; BODY = "NO"; PRINT="NO" } if ( $1 == MAIN_KEY ) { if ( $2 == VALIDATION ) { PRINT = "YES" $1 = ""; $2 = "" print "\n\n"$0"\n" > OUT_FILE } } if ( $1 == BEGIN_BDY ) { getline if ( $0 == "" ) { HEAD = "NO"; BODY = "YES" } else { HEAD = "NO"; BODY = "NO"; PRINT="NO" } } if ( BODY == "YES" && PRINT == "YES" ) { print $0 >> OUT_FILE } } |
Tal vez administramos una lista de correo. Tal vez,
de vez en cuando, se envían a la lista mensajes especiales (p.e. resúmenes
mensuales) con algún formato determinado (p.e. un subject tipo '[RESUMEN
MENSUAL] mes , dept'). Y de repente, se nos ocurre a fin de año recopilar
todos los resúmenes, separándolos de los demás mensajes.
Esto podemos hacerlo usando el awk con el spool del mail y el programa
que tenemos a la izquierda
Hacer que cada resumen vaya a un fichero requiere tres líneas adicionales, y hacer también que, por ejemplo, cada departamento vaya a un fichero diferente supone unos pocos caracteres más. |
NOTA: Todo este ejemplo está basado en cómo creo yo que estan estructurados los mails en el spool. Realmente no se como lo hacen, aunque me funciona (de nuevo, en algunos casos fallará, como siempre). |
Programas como éstos sólo necesitan 5 minutos pensando y 5 escribiendo
(o más de 20 minutos sin pensar, mediante ensayo y error que es como resulta
más divertido).
Si hay alguna forma de hacerlo en menos tiempo, quiero saberla.
He usado el awk
para muchas otras cosas (como generación automática
de páginas web con información obtenida de una base de datos) y se lo
suficiente de programación como para estar seguro de que se pueden hacer
con él cosas que ni siquiera se me han ocurrido.
Sólo hay que dejar volar la imaginación.
awk
Hasta ahora, casi todos los ejemplos expuestos procesan todas las lineas del fichero de entrada. Pero, como claramente explica la página de manual, es posible hacer que un cierto grupo de comandos procese tan sólo unas ciertas líneas por el simple método de incluir la condición antes de los comandos, al modo del segundo de los ejemplos anteriores. La condición que debe satisfacer la línea puede llegar a ser bastante flexible, desde una expresión regular, hasta un test sobre los contenidos de alguno de los campos, pudiendo agruparse condiciones en base a operadores lógicos.
awk
como lenguaje de programación
Como todo lenguaje de programación, awk
implementa todas las
estructuras de control necesarias, así como un conjunto de operadores y
funciones predefinidas, para manejar números y cadenas. Su sintaxis es en
general muy parecida a la del C, aunque difiere de él en algunos aspectos.
Y, por supuesto, también es posible incluir funciones definidas por el
usuario, usando la palabra function, y escribiendo los comandos como
si se tratara de procesar una línea normal del fichero de entrada. E,
igualmente, aparte de las variables escalares habituales, tambíen es
capaz de manejar
Como suele pasar con todos los lenguajes, hay una cierta serie de funciones
que son bastante comunes, y llega un momento en que cortar y pegar no es
la mejor forma de hacer las cosas. Para eso se inventaron las librerías.
Y, al menos con la versión GNU de awk
, es posible incluirlas
dentro del programa awk
. Pero eso es usar awk
como una herramienta de trabajo mucho más seria de lo que se pretende mostrar
en este artículo, aunque deja claro el nivel de complejidad que puede llegar
a alcanzar el awk
.
Ciertamente, puede no ser tan potente como numerosas herramientas que se pueden usar con la misma finalidad. Pero tiene la enorme ventaja de que, en un tiempo realmente corto, permite escribir programas que, aunque tal vez sean de un solo uso, están totalmente adaptados a nuestras necesidades, que en muchas ocasiones son sumamente sencillas.
awk
es ideal para los propósitos con los que se diseño: leer
ficheros línea por línea y procesar en base a los
Ficheros del sistema como el /etc/password
y muchos otros,
resultan sumamente fáciles de tratar mediante el awk
,
sin recurrir a nada más.
Y desde luego que awk
no es el mejor. Hay varios
lenguajes de scripting con capacidades mucho mayores.
Pero awk
sigue teniendo la ventaja
de ser siempre accesible en cualquier instalación, por mínima que esta
sea.
Este tipo de comandos tan básicos no suelen estar excesivamente documentados, pero siempre se puede encontrar algo buscando por ahí.
awk
no es igual en todos los *nix, pero siempre hay una
forma de saber exactamente qué podemos hacer con el del nuestro particular:
man awk
;
gawk
, y media docena más.
En general, todos los libros y manuales de unix mencionan estos comandos. Pero sólo algunos de ellos profundizan un poco y dan información útil. Lo mejor, hojear todos aquellos que pasen por nuestras manos, pues nunca se sabe donde podemos encontrar información valiosa.
Contactar con el equipo de LinuFocus
© Javier Palacios Bermejo LinuxFocus 1999 |
1999-06-05, generated by lfparser version 0.6