[caracas-pm] Ejercicio del mes [SOLUCION]

Ernesto Hernández-Novich emhn at telcel.net.ve
Sun Apr 10 09:02:35 PDT 2005


On Sat, 2005-03-26 at 11:46 -0400, Ernesto Hernández-Novich wrote:
> El comando last permite ver el historial de conexiones de usuarios al
> sistema. En particular, si uno utiliza el pseudousuario 'reboot' puede
> saber las veces en que se reinició el sistema.
> 
> reboot   system boot  2.6.11   Fri Mar 11 17:13         (14+18:29)
> reboot   system boot  2.6.11   Thu Mar  3 11:11         (8+06:00)
> reboot   system boot  2.6.11   Wed Mar  2 13:18         (9+03:53)
> 
> La última columna representa el número de días, horas y minutos que el
> sistema estuvo encendido hasta el siguiente reboot (y para el caso del
> más reciente, cuánto tiempo tiene encendido).
> 
> Escriban un programa que reciba como entrada el listado producido por el
> comando 'last reboot' y produzca un breve reporte (formato libre) que
> indique la cantidad de reinicios, máximo tiempo en servicio, mínimo
> tiempo en servicio y tiempo promedio en servicio.

Mi solución a continuación. Algunas observaciones que hacen posible
escribirlo de manera compacta (hablamos de ellas en la sesión de
one-liners, y el ejercicio fue seleccionado adrede para aprovecharlas):

1. Como sólo me interesa imprimir resultados calculados, aprovecho la
   opción -n, y sólo hago print explícito. Más aún, sólo me interesa
   imprimir al final, por tanto usamos un bloque END.
2. Para ahorrarme el chomp y los \n en cada print, uso el flag -l.
3. Me interesa la última columna, si uso el flag -a las columnas van
   _automáticamente_ al arreglo @F, y $F[-1] es la última. No hay que
   complicarse con splits, substrs, regexes, etc.
4. Normalizo las medidas a minutos usando un regex y aritmética.
5. Puede hacerse todo en una sola pasada porque:

  - Sólo me interesan líneas que digan 'reboot'.
  - El número de líneas contempladas se cuenta a medida que se
    consideran.
  - Si sumo en un total todas las medidas, al final simplemente debo
    dividir entre el total de líneas y tengo el promedio.
  - Si tengo un "máximo hasta ahora", lo reemplazo por el nuevo valor
    sólo si el actual es mayor que el anterior. Lo mismo se puede decir
    de un "mínimo hasta ahora". Para ello necesito que el máximo
    comience en cero (así el primer valor será mayor) y que el mínimo
    comience en un valor absurdamente alto (así el primer valor será
    menor). Lo primero es un hecho gracias a Perl, lo segundo debo
    lograrlo inicializando en un bloque BEGIN.

6. Uso el excelente módulo Date::Manip para convertir de minutos a días,
   minutos y segundos.

Así, menos de una pantalla...

#!/usr/bin/perl -lan
use Date::Manip qw(ParseDateDelta Delta_Format);
$ENV{TZ} = '-0400';
sub m2d {
  my $min = shift;
  my ($d,$h,$m) = Delta_Format(ParseDateDelta("$min min"),0,qw(%dh %hv %
md));
  return "(" . (($d > 0) ? "$d+" : "") . "$h:$m)";
}
BEGIN { $i = 1e100; }
next unless /reboot/;
$n++;
$F[-1] =~ /\(((\d+)\+)?(\d+):(\d+)\)/;
$v = $2*24*60 + $3*60 + $4;
$x = $v unless $x > $v;
$i = $v unless $i < $v;
$a += $v;
END {
  print "n: $n max: ",m2d($x)," min: ",m2d($i)," avg: ",m2d($a/$n);
}
-- 
Ernesto Hernández-Novich - On Linux 2.6.11 i686 - Unix: Live free or die!
Geek by nature, Linux by choice, Debian of course.
If you can't apt-get it, it isn't useful or doesn't exist.
GPG Key Fingerprint = 438C 49A2 A8C7 E7D7 1500 C507 96D6 A3D6 2F4C 85E3



More information about the caracas-pm mailing list