[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