AWK |
Vamos a modificar el programa 'contabil1.awk' para procesar solo los registros de consumo de luz, vamos a mejorar el
formato de salida, vamos a incluir un contador de registros seleccionados, un contador de consumo de luz, y al final
obtendremos el consumo total y el consumo promedio de luz. Lo llamaremos 'contabil2.awk'
BEGIN { FS="\|" ; cont_reg_luz=0; cont_importe_luz=0; } /LUZ/ { cont_reg_luz = cont_reg_luz + 1 ; cont_importe_luz = cont_importe_luz + $3 ; printf ("%3d, %3d, %s, %s, %s, %10d\n", NR, cont_reg_luz, $1, $2, $3, cont_importe_luz); } END { printf ("Consumo promedio = %d\n", cont_importe_luz / cont_reg_luz) ;} |
5, 1, 05-01-2000, LUZ , -15797, -15797 9, 2, 25-02-2000, LUZ , -12475, -28272 16, 3, 02-05-2000, LUZ , -11449, -39721 23, 4, 04-07-2000, LUZ , -12403, -52124 29, 5, 04-09-2000, LUZ , -12168, -64292 Consumo promedio = -12858 |
BEGIN { FS="\|" ; cont_importe=0; } /[0-9][0-9]\-[0-9][0-9]\-[0-9][0-9][0-9][0-9]\|/ { cont_importe = cont_importe + $3 ; printf ("%3d, %s, %s, %s, %10d\n", NR, $1, $2, $3, cont_importe); } |
3, 01-01-2004, - , 96, 96 4, 16-12-2004, AGUA , -14650, -14554 5, 05-01-2004, LUZ , -15797, -30351 6, 24-01-2004, GAS , -34175, -64526 7, 27-01-2004, INGRESO , 141200, 76674 8, 01-02-2004, MENS , -96092, -19418 9, 25-02-2004, LUZ , -12475, -31893 10, 01-03-2004, MENS , -96092, -127985 11, 06-03-2004, INGRESO , 101300, -26685 12, 01-04-2004, MENS , -96092, -122777 13, 06-04-2004, AGUA , -15859, -138636 14, 07-04-2004, INGRESO , 134000, -4636 15, 01-05-2004, MENS , -98975, -103611 16, 02-05-2004, LUZ , -11449, -115060 17, 09-05-2004, INGRESO , 95000, -20060 18, 23-05-2004, GAS , -21428, -41488 19, 25-05-2004, GAS , -16452, -57940 20, 01-06-2004, MENS , -98975, -156915 21, 07-06-2004, INGRESO , 130000, -26915 22, 01-07-2004, MENS , -98975, -125890 23, 04-07-2004, LUZ , -12403, -138293 24, 07-07-2004, AGUA , -5561, -143854 25, 10-07-2004, INGRESO , 99000, -44854 26, 24-07-2004, GAS , -11948, -56802 27, 01-08-2004, MENS , -98975, -155777 28, 10-08-2004, INGRESO , 122355, -33422 29, 04-09-2004, LUZ , -12168, -45590 30, 10-09-2004, INGRESO , 129000, 83410 31, 19-09-2004, AGUA , -10529, 72881 32, 28-09-2004, GAS , -2620, 70261 33, 01-10-2004, MENS , -98975, -28714 34, 10-10-2004, INGRESO , 112000, 83286
4.14.10 Sentencias condicionales y bucles
'awk' es un lenguaje muy completo y no podía faltar las sentencias de ejecución condicional y de ejecución en bucle.
Algunos de los conceptos que vamos a comentar ya los hemos visto cuando hablamos de la programación en bash y no vamos
a explicar con demasiado detalle cada tipo de sentencia. La sintaxis que usa awk no se parece a la sintaxis que ya
hemos visto para bash. Se parece más a la sintaxis del lenguaje C. De todas formas los conceptos ya nos resultan
familiares y usaremos algunos ejemplos para ilustrarlos.
Empezaremos describiendo la sintaxis de cada tipo de sentencia. Denominaremos acción a una sentencia simple o a una
sentencia compuesta de la forma '{ sentencia1 ; sentencia2 ; ... }'
Sentencia condicional 'if'
if ( expresión_lógica )
accion1
[ else
accion2 ]
Sentencia condicional con los operadores '?' y ':'
expresion_lógica ? accion1 : accion2
Bucle 'while'
while ( expresión_lógica )
accion
Bucle 'do' 'while'
do
accion
while ( expresión_lógica )
Bucle 'for'
for ( inicializar_contador ; comprobar_contador ; modificar_contador )
accion
Dentro de los bucles podemos usar break para forzar la salida de un bucle o continue para saltar a la siguiente
iteración.
Veremos de momento tan solo un ejemplo para la sentencia condicional 'if'.
Edite el siguiente fichero que llamaremos 'contabil4.awk'
BEGIN { FS="\|" ; } /[0-9][0-9]\-[0-9][0-9]\-[0-9][0-9][0-9][0-9]\|/ { if ( $3 >= 0) { printf ("%3d, %s, %s, %s\n", NR, $1, $2, $3); } } |
3, 01-01-1999, - , 96 7, 27-01-2000, INGRESO , 141200 11, 06-03-2000, INGRESO , 101300 14, 07-04-2000, INGRESO , 134000 17, 09-05-2000, INGRESO , 95000 21, 07-06-2000, INGRESO , 130000 25, 10-07-2000, INGRESO , 99000 28, 10-08-2000, INGRESO , 122355 30, 10-09-2000, INGRESO , 129000 34, 10-10-2000, INGRESO , 112000 |
4.14.11 Pasar valores al script awk
En ocasiones puede resultar interesante poder pasar algún valor al script awk. Vamos a modificar el programa anterior
para que muestre los registros con un importe superior a un valor que pasaremos por parámetro.
Edite el siguiente fichero que llamaremos 'contabil5.awk'
BEGIN { FS="\|" ; } /[0-9][0-9]\-[0-9][0-9]\-[0-9][0-9][0-9][0-9]\|/ { if ( $3 >= minimo && $3 <= maximo ) { printf ("%3d, %s, %s, %s\n", NR, $1, $2, $3); } } |
11, 06-03-2000, INGRESO , 101300 34, 10-10-2000, INGRESO , 112000 |
BEGIN { FS = delimitador ; } /[0-9][0-9]\-[0-9][0-9]\-[0-9][0-9][0-9][0-9]\|/ { if ( $3 >= minimo && $3 <= maximo ) { printf ("%3d, %s, %s, %s\n", NR, $1, $2, $3); } } |
11, 06-03-2000, INGRESO , 101300 34, 10-10-2000, INGRESO , 112000 |
4.14.12 Declaración de funciones
Como es lógico 'awk' permite la declaración de funciones. Normalmente se recurre a implementar una función cuando
necesitamos una funcionalidad que el lenguaje no proporciona de forma predefinida o cuando queremos estructurar el
código de un programa grande en fragmentos más pequeños y por tanto más manejables.
La sintaxis es muy sencilla.
function nombre_de_la_función ( lista_de_parámetros ) {
sentencias
}
Por ejemplo para declarar una función que retorne un número aleatorio entre 1 y 6.
Edite un fichero con nombre 'dado.awk'.
function aleatorio ( minimo, maximo ){ return ( ( ( maximo - minimo + 1 ) * rand () ) + minimo ) ; } END { for (i=0; i<10; i++){ printf ("%3d) Entre 1 y 6 = %3d Entre 5 y 15 =%3d\n",i, aleatorio (1, 6), aleatorio(5, 15)); } } |
0) Entre 1 y 6 = 5 Entre 5 y 15 = 12 1) Entre 1 y 6 = 6 Entre 5 y 15 = 7 2) Entre 1 y 6 = 6 Entre 5 y 15 = 7 3) Entre 1 y 6 = 5 Entre 5 y 15 = 13 4) Entre 1 y 6 = 3 Entre 5 y 15 = 8 5) Entre 1 y 6 = 3 Entre 5 y 15 = 6 6) Entre 1 y 6 = 4 Entre 5 y 15 = 7 7) Entre 1 y 6 = 6 Entre 5 y 15 = 7 8) Entre 1 y 6 = 5 Entre 5 y 15 = 6 9) Entre 1 y 6 = 3 Entre 5 y 15 = 10 |
Esta es una función fácil de usar que nos permite ejecutar un comando del sistema operativo. En caso de éxito retorna 0,
y en caso de error retornará un valor distinto de cero.
$ awk ' BEGIN { if (system("ls") !=0) printf ("Error de ejecución\n"); }'
Por ejemplo si quisiéramos verificar la existencia de un fichero almacenado en la variable 'nombre_fich'
tendríamos que hacer
if (system("test -r " nombre_fich)) { fprintf ("%s no encontrado\n", nombre_fich); } |
4.14.14 La función getline y otras funciones avanzadas
Este es es un apartado en el que más que explicar cosas nos vamos a limitar a mencionar ciertas posibilidades. No
podemos dedicar demasiado espacio a este tipo de cuestiones avanzadas pero si con lo que en este apartado contemos
conseguimos ponerle los dientes largos nos daremos por satisfechos aunque no entienda una palabra.
En primer lugar hay que advertir que 'getline' que al igual que otras funciones devuelve un valor pero su
sintaxis no es una típica sintaxis de función. No se usa como 'getline()' sino como una sentencia.
Esta función retorna 1 si lee una línea, 0 si alcanza el fin de la entrada de datos y -1 si se produce un error.
Usada simplemente como 'getline' sin nada más lee la siguiente linea de la entrada asignando $0 y desglosando los
campos en $1, $2, $3, etc..
Se puede asignar el valor completo de la línea leída a una variable con 'getline variable' evitando de esta forma
alterar el valor de $0.
Se puede leer de un fichero usando el operador redirección. 'getline < "fichero"'. Se puede simbolizar la entrada
estándar como "-"
Se puede leer desde un pipe. '"whoami" | getline usuario'.
Edite un fichero llamado 'tipo_usuario.awk'.
BEGIN {"whoami" | getline usuario if ( usuario ~ /root/ ) { printf ("Soy superusuario\n"); } else{ printf ("Soy usuario normal\n"); } } |
Los array permiten el almacenamiento de una serie de elementos que pueden ser accedidos mediante un índice. En realidad
los arrays de awk son más potentes que los arrays que vimos cuando estudiamos la programación de la bourne-shell donde
los índices de un array eran siempre números enteros. Vamos a usar en primer lugar los arrays de esta forma. Es decir
nos vamos a limitar en los primeros ejemplos a usar números enteros como índices.
Vamos a usar awk para procesar la salida obtenida con 'ps'. Primero vamos a suponer que obtenemos un listado completo
de los procesos del sistema en formato largo. Si intenta realizar este ejemplo obtendrá un resultado necesariamente
diferente.
$ ps axl > salida-ps-axl ; cat salida-ps-axl
FLAGS UID PID PPID PRI NI SIZE RSS WCHAN STA TTY TIME COMMAND 100 0 1 0 0 0 756 0 do_select SW ? 0:03 (init) 40 0 2 1 0 0 0 0 bdflush SW ? 0:18 (kflushd) 40 0 3 1 0 0 0 0 kupdate SW ? 0:18 (kupdate) 840 0 4 1 0 0 0 0 kpiod SW ? 0:00 (kpiod) 840 0 5 1 0 0 0 0 kswapd SW ? 0:15 (kswapd) 140 0 186 1 0 0 900 200 do_select S ? 0:00 /sbin/sys 140 0 188 1 0 0 1016 0 do_syslog SW ? 0:00 (klogd) 140 1 193 1 0 0 780 0 do_select SW ? 0:00 (portmap) 140 0 195 1 0 0 860 0 do_select SW ? 0:00 (inetd) 140 0 200 1 0 0 764 108 nanosleep S ? 0:00 /usr/sbin 140 0 210 1 0 0 908 0 do_select SW ? 0:00 (lpd) 40 31 226 1 0 0 3784 0 do_select SW ? 0:00 (postmast) 140 0 237 1 5 0 1728 316 do_select S ? 0:00 sendmail: 140 0 241 1 0 0 1292 184 do_select S ? 0:01 /usr/sbin 140 0 244 1 0 0 1544 56 do_select S ? 0:00 /usr/bin/ 140 1 254 1 0 0 840 96 nanosleep S ? 0:00 /usr/sbin 140 0 257 1 5 0 860 164 nanosleep S ? 0:00 /usr/sbin 140 0 262 1 0 0 1780 60 do_select S ? 0:07 /usr/sbin 100 0 268 1 0 0 1964 616 read_chan S 1 0:00 -bash 100 0 269 1 0 0 836 0 read_chan SW 2 0:00 (getty) 100 1001 270 1 0 0 2096 724 wait4 S 3 0:00 -bash 100 0 271 1 0 0 836 0 read_chan SW 4 0:00 (getty) 100 0 272 1 0 0 836 0 read_chan SW 5 0:00 (getty) 100 1001 273 1 0 0 2088 1408 wait4 S 6 0:00 -bash 140 33 274 262 0 0 179 0 wait_for_co SW ? 0:00 (apache) 140 33 275 262 0 0 1792 0 flock_lock_ SW ? 0:00 (apache) 140 33 276 262 0 0 1792 0 flock_lock_ SW ? 0:00 (apache) 140 33 277 262 0 0 1792 0 flock_lock_ SW ? 0:00 (apache) 140 33 278 262 0 0 1792 0 flock_lock_ SW ? 0:00 (apache) 0 1001 916 270 0 0 3536 1640 do_select S 3 0:00 vi awk1.d 0 1001 1029 273 0 0 1916 668 wait4 S 6 0:00 xinit /ho 100 0 1034 1029 12 0 8824 3280 do_select S ? 0:02 X :0 -bpp 0 1001 1037 1029 0 0 4620 2748 do_select S 6 0:01 mwm 40 1001 1042 1037 0 0 1728 924 wait4 S 6 0:00 bash /hom 40 1001 1045 1037 0 0 1728 924 wait4 S 6 0:00 bash /hom 0 0 1050 1042 0 0 2976 1872 do_select S 6 0:00 xterm -ls 0 1001 1058 1045 0 0 2320 1220 do_select S 6 0:00 xclock -d 100 1001 1051 1050 14 0 2080 1400 wait4 S p0 0:00 -bash 100 1001 1074 1051 17 0 1068 528 R p0 0:00 ps axl
Para dar una idea de la situación de parentescos entre los distintos procesos mostramos la salida obtenida con el
comando 'pstree' ejecutado desde la misma sesión de xterm que en el caso anterior.
$ pstree -p > salida-pstree-p ; cat salida-pstree-p
init(1)-+-apache(262)-+-apache(274) | |-apache(275) | |-apache(276) | |-apache(277) | `-apache(278) |-atd(254) |-bash(268) |-bash(270)--vi(916) |-bash(273)--xinit(1029)-+-XF86_S3V(1034) | `-mwm(1037)-+-.xinitrc(1042)--xterm(1050)--bash(1051)--pstree(1068) | `-.xinitrc(1045)--xclock(1058) |-cron(257) |-getty(269) |-getty(271) |-getty(272) |-gpm(200) |-inetd(195) |-kflushd(2 |-klogd(188) |-kpiod(4) |-kswapd(5) |-kupdate(3) |-lpd(210) |-portmap(193 |-postmaster(226) |-sendmail(237) |-sshd(241) |-syslogd(186) `-xfs(244) |
BEGIN { ind=0; } function padre(p){ for (i=0; i", proc); proc= padre(proc); } while ( proc >= 1 ) printf ("\n\n");} |
1051->1050->1042->1037->1029->273->1-> |
FTLSUSE |CURSOS |FTLinuxCourse para SuSE | 11800 FTLREDH |CURSOS |FTLinuxCourse para RedHat | 11800 ASUSCOM |HARDWARE|Asuscom ISDNLink 128k Adapter (PCI) | 6865 RAILROAD|JUEGOCOM|Railroad Tycoon (Gold Edition) | 7700 CIVILIZ |JUEGOCOM|Civilization: Call to power | 7700 MYTHII |JUEGOCOM|Myth II | 7700 LIAPPDEV|LIBROS |Linux Application Development (537 Páginas) | 11000 CONECT01|LIBROS |Guía del Usuario de Linux (413 Páginas) | 5300 CONECT03|LIBROS |Guía del Servidor (Conectiva Linux 437 Páginas) | 5300 CONECT02|LIBROS |Guía del Administrador de redes (465 Páginas) | 5300 LIUSRESU|LIBROS |Linux User's Resource (795 Páginas) | 12000 RH70DLUX|LINUXCOM|RedHat Linux 7.0 Deluxe en español | 9600 RH70PROF|LINUXCOM|RedHat Linux 7.0 Profesional en Español | 20000 SUSE70 |LINUXCOM|Suse Linux 7.0 (6CDs)(Version española) | 6850 RTIME22 |LINUXCOM|RealTime 2.2 (1CD) | 13000 CONCT50E|LINUXCOM|Conectiva Linux 5.0 Versión Económica Español (6CDs) | 5200 CITIUS22|LINUXCOM|Linux Citius 2.2 | 7750 TRBLIW60|LINUXCOM|Turbolinux Workstation 6.0 | 6500 MOTIF |LINUXCOM|Motif Complete | 22000 CONCTSRV|LINUXCOM|Conectiva Linux Ed.Servidor (Español 3CDs + 4 Manua | 27500 RHORA8I |LINUXCOM|RedHat Linux Enterprise Edition optimized for Oracle8i |270000 MANDRA72|LINUXCOM|Mandrake 7.2 (7CDs) PowerPack Deluxe (versión española| 8300 PINGUINO|SUSEPROM|Pingüino de peluche | 6000
BEGIN { FS="[\ \t]*\|[\ \t]*" ; while ( getline < "articulos.dat" > 0){ artic[$1]= "(" $4 " Ptas + Iva) " $3; printf ("%s ", $1); } for (;;){printf ("\n\nIntroduzca un código de artículo: "); getline codigo ; if (codigo == "" ) break; printf ("\n<%s>\n%s", codigo, artic[codigo]);} } |
$ awk -f articulos.awk FTLSUSE FTLREDH ASUSCOM RAILROAD CIVILIZ MYTHII LIAPPDEV CONECT01 CONECT03 CONECT02 LIUSRESU RH70DLUX RH70PROF SUSE70 RTIME22 CONCT50E CITIUS22 TRBLIW60 MOTIF CONCTSRV RHORA8I MANDRA72 PINGUINO Introduzca un código de artículo : RH70PROF (20000 Ptas + Iva) RedHat Linux 7.0 Profesional en Español Introduzca un código de artículo : CITIUS22 (7750 Ptas + Iva) Linux Citius 2.2 Introduzca un código de artículo : $ |