awk: getline + system

Ayer estuve ayudando a una compañera a desarrollar un pequeño script que hiciera algo útil para administración de sistemas Linux. Las condiciones que se ponían era que tenía que usar awk con getline y system. Puesto que nunca hasta ese momento usé tales cosas, me pareció interesante. Hoy voy a comentar aquí, para todos aquellos a los que le haga falta lo que se hizo.
El script que nos propusimos hacer Pepe (Doval) y yo era algo muy simple: Contar la cantidad de ficheros que cada usuario del sistema tenía en una carpeta. Dicha carpeta era introducida como parámetro al script. A priori algo muy sencillo que se podría escribir en bash de manera muy simple.
for i in `awk 'BEGIN{FS=":"} {print $1}' /etc/passwd`
do
   echo -n -e "user: $i.\tFicheros: "
   find $1 -type f -user $i | wc -l
done
El detalle es que así no cumplíamos los requisitos propuestos, que no eran otros que usar las funciones getline y system. Comenzamos por lo tanto a intentar crear un script que lo hiciese. La primera aproximación fue la siguiente:
#/bin/bash
awk -v raiz=$1 'BEGIN { FS=":"
   if(system("test -d "raiz)!=0)
      print ("No existe el directorio");
   else
   {
      while (getline < "/etc/passwd" != 0)
      {
         system( "find "raiz" -user "$1" | wc -l" | getline aa
         print "El usuario " $1 " tiene "aa" ficheros en " raiz;
      }
   }
}'
Puesto que el comando find sí se podía usar, lo aprovechamos (flag -user) y con ello hacemos lo que se nos pide de forma muy sencilla. El problema de esta solución es que la línea del segundo system, la que realiza el find, no funciona, puesto que el paso de parámetros así en awk no es válido.
La solución final por que se se optó no fue otra que hacer el script directamente en awk. Así evitaríamos el paso intermedio de enlace entre bash y awk. Desde luego, una solución más limpia.
El resultado final, tendría por ejemplo el siguiente aspecto:
BEGIN { FS=":"
   print "Introduce el directorio a evaluar"
   getline dir < "-"
   if(system("test -d "dir) != 0)
      print ("No existe el directorio");
   else
   {
      while ((getline < "/etc/passwd") != 0)
      {
         cmd = sprintf("find %s -type f -user %s | wc -l",dir,$1);
         cmd | getline line
         if(line != 0)
            print "El usuario "$1" tiene "line" ficheros en "dir"."
      }
   }
}
Acepto que no es eficiente ni queda bonito el código, pero hace lo que tiene que hacer. Un ejemplo de salida:
shell> awk -f prueba.awk
Introduce el directorio a evaluar
/home
El usuario javier tiene 8297 ficheros en /home.
El usuario user tiene 7949 ficheros en /home.
El usuario root tiene 3 ficheros en /home.
El usuario www-data tiene 4 ficheros en /home.


1 comentario :

  1. Por alusiones y porque si no no quedas contento, te dejaré un comentario.

    Solo apuntar la solución que yo propuse (y que finalmente fue entregada):

    awk -v dir=$1 '
    BEGIN { FS=":" }
    { cmd = ("find " dir " -type f -user " $1 " 2>/dev/null | wc -l")
    cmd | getline num
    if (num != 0)
    print "El usuario " $1 " tiene " num " fichero(s) en " dir "."
    }

    No utiliza 'system' porque desde mi punto de vista la frase "con getline y comandos del sistema" del enunciado se refiere a usar getline con comandos (una de las 6 formas de usar getline), que es lo que se hace en la línea 'cmd | getline num'.

    Por cierto, vaya mierda de formulario para los comentarios, que no puedo indentar el código.

    ResponderEliminar

Formulario de contacto

Nombre

Correo electrónico *

Mensaje *

Últimos comentarios