Copyright 2005-2008 Sergio González Durán
Se concede permiso para copiar, distribuir y/o modificar este documento siempre y cuando se cite al autor y la fuente de linuxtotal.com.mx y según los términos de la GNU Free Documentation License, Versión 1.2 o cualquiera posterior publicada por la Free Software Foundation.
autor: sergio.gonzalez.duran@gmail.com
awk
o la versión GNU gawk
es más que un simple comando de procesamiento de patrones, es todo un lenguaje de análisis semántico. Su dominio es como aprender todo un lenguaje de programación, pero en esta ocasión veremos unos ejemplos de su potencia en unos casos sencillos de análisis de patrones de cadenas, espero te sirvan de base para que puedas aprender aun más sobre esta interesante herramienta.
Ejemplo 1: Una lista personalizada de usuarios para HTML
Una línea típica de /etc/passwd es como la siguiente:
luis:x:504:504:Luis Hernandez:/home/luis:/bin/bash
Bien, supongamos que deseamos un listado de todos los usuarios normales (personas) del sistema, pero solo necesitamos su nombre de usuario, su nombre o real y su shell por defecto, es decir, si vemos la línea anterior, la separación entre campos es ":" asi que para nuestro reporte queremos el campo 1,5 y 7. Pero además, este reporte será parte de una tabla HTML, asi que sería bueno si puderian incluirse de una vez las etquietas " y " necesarias de una vez. Es decir, el resultado deseado es el siguiente:
luisLuis Hernandez/bin/bash
El primer paso es determinar los usuarios normales del sistema, podríamos usar un grep "home" /etc/passw | gawk ...
, pero podría haber usuarios que tengan su HOME en otra ubicación, además se trata de usar solo awk
, asi que lo primero que entenderemos es que los campos obtenidos del resultado de un comando awk, se numeran por $1, $2, etc. y el delimitador de campos se indica mediante la variable "FS".
#> gawk '{print $3}' FS=":" /etc/passwd
0
1
2
...
81
86
500
501
502
503
504
Aunque no muy útil todavía, podemos ver como seleccionamos el caracter separador FS=":", que viene de 'Field Separator', y tenemos indicada una acción '{print $3}', que significa imprime el campo 3. Aunque realmente no lo queremos imprimir, lo queremos evaluar, y si deseamos imprimir el $1, $5 y $7 que se mencionarion previamente, asi que agregamos una expresión de evaluación antes de la acción:
#> awk '$3 >= 500 {print $1 $5 $7 }' FS=":" /etc/passwd
sergonSergio Gonzalez/bin/bash
valeriaValeria Perez/bin/bash
fernandaFernanda Lozano/bin/sh
alejandraAlejandra Lopez/bin/nologin
luisLuis Hernandez/bin/bash
Mucho mejor, agregamos '$3 >= 500' previo a la acción (que es imprimir lo que deseamos), ya que como se sabe en la mayoría de distros modernas, los usuarios normales del sistema se numeran del 500 en adelante (más sobre administración de usuarios). Nótese que los campos en el resultado salen pegados, es necesario agregar entre comillas " ", ya sea un espacio o lo que se desee, en este caso etiquetas de tablas de HTML y además ordenaremos "sort" los registros obtenidos:
#> awk '$3 >= 500 {print ""$1""$5""$7"" | "sort" }' FS=":" /etc/passwd
alejandraAlejandra Lopez/bin/nologin
fernandaFernanda Lozano/bin/sh
luisLuis Hernandez/bin/bash
sergonSergio Gonzalez/bin/bash
valeriaValeria Perez/bin/bash
Ejemplo 2: Una lista personalizado de usuarios para reporte
Ahora veamos como crear una lista similar a la anterior pero especificando un par de líneas de títulos al inicio:
#> awk 'BEGIN { print "Usuario UID Shell\n------- --- -----" } $3 >= 500 { print $1, $3, $7 | "sort -r"}' FS=":" /etc/passwd
Usuario UID Shell
------- --- -----
valeria 501 /bin/bash
sergon 500 /bin/bash
luis 504 /bin/bash
fernanda 502 /bin/sh
alejandra 503 /bin/nologin
Iniciamos con la sentencia BEGIN (que tiene varios usos, checa el manual) que en este caso nos permite indicar lo que se imprimirá una sola vez, en este caso los títulos, un retorno '\n' y guiones para separar los títulos, después viene de nuevo la evaluación ya conocida y ahora al indicar la impresión de los campos 'print $1, $3, $7', los separé por comas, que automáticamente añade un espacio, y solo para aumentar el ejemplo, el ordenamiento es ahora al revés 'sort -r'.
Pero podemos ver en la salida, que los registros no se acomodan bien con respecto a los títulos, con dos o tres campos tal vez resulte fácil añadir espacios o tabuladores '\t' para hacer el acomodo, pero en varios campos será realmente frustrante lograrlo. Mejor usamos 'printf':
#> awk 'BEGIN { print "Usuario UID Shell\n------------ ---- ----------" } $3 >= 500 \
{ printf "%12s %4d %10s\n", $1, $3, $7 | "sort -r"}' FS=":" /etc/passwd
Usuario UID Shell
------------ ---- ----------
valeria 501 /bin/bash
sergon 500 /bin/bash
luis 504 /bin/bash
fernanda 502 /bin/sh
alejandra 503 /bin/nologin
Sigo usando 'print' para los títulos, pero ahora uso 'printf' que me permite formatear la salida, la sintaxis es 'printf "formato", $1, $2'. Cada formato comienza con '%' después el número de posiciones seguido del tipo de campo 's' para string, 'd' para enteros, etc. '%12s' 12 posiciones de tipo cadena. (checar el manual para el resto de formatos de printf), termino el formato con un salto de línea '\n'. Pero como se puede apreciar por defecto las cadenas se justifican a la derecha y en este caso las deseamos a la izquierda, esto se arregla agregando un '-' guión en el indicador de formato de la siguiente manera '%-12s', el resultado correcto sería el siguiente:
# awk 'BEGIN { print "Usuario UID Shell\n------------ ---- ----------" } $3 >= 500 \
{ printf "%-12s %4d %-10s\n", $1, $3, $7 | "sort -r"}' FS=":" /etc/passwd
Usuario UID Shell
------------ ---- ----------
valeria 501 /bin/bash
sergon 500 /bin/bash
luis 504 /bin/bash
fernanda 502 /bin/sh
alejandra 503 /bin/nologin
Ejemplo 3: Extrayendo campos sin posición fija
Hay ocasiones en que no se tiene exactamente la posición de los campos a extraer, asi que usaremos otra técnica basada en el número total de campos encontrados. En este caso, necesitamos la variable "NF" (Number of Fields) que representa el total de campos encontrados. Y usaremos como caracter separador FS el espacio, es decir FS=" ", pero dado que el espacio es el separador por defecto no es necesario indicarlo.
Como ejemplo, veamos la salida del comando uptime
:
$> uptime
19:32:15 up 2:28, 1 user, load average: 1.75, 1.54, 1.54
Y deseamos crear un pequeño script que de como resultado el siguiente:
$> ./carga
carga actual del sistema: 1min=1.75, 5min=1.54, 15min=1.54
Como se puede observar, se necesitan los tres últimos campos, aparentemente los campos $8, $9 y $10. Pero el problema esta en que conforme pasa el tiempo, el comando uptime
mostrará de hecho más campos, ya que el que en este momento es el $3 '2:28' después de unos días puede quedar asi '3 days 3:50', asi que al usar NF nos dará el total de campos, veamos:
$> uptime | gawk '{print NF}'
gt; uptime | gawk '{print NF}'
10
(Podemos usar NF no como varibale del total de campos sino como variable de contenido con $NF)
$> uptime | gawk '{print $NF}'
1.33
Entendiendo lo anterior, es fácil de deducir entonces que los campos requeridos son entonces NF-2, NF-1, NF es decir, los tres últimos, y no importará cuantos campos haya en el resultado.
$> uptime | gawk '{print $(NF - 2), $(NF - 1), $NF}'
0.72, 0.54, 0.47
El script quedaría entonces así:
#!/bin/bash
echo "Carga actual del sistema: "
uptime | gawk '{print "1min:"$(NF - 2), "5min:"$(NF - 1), "15min:"$NF}'
Fuente: Linuxtotal.com.mx
1 comentarios:
Gracias por el post solo gueno susus
Post a Comment