Mail list statistics
An ancient script which I used to compute some stats on who posted how much in mailing lists. Get it here, browse the files and examples, or see the code… (sorry comments in spanish)
#!/bin/bash## Miguel de Benito <nonick AT 8027 DOT org>## [ MBD Wed Jan 15 03:59:51 CET 2003 ]# Computa una lista de las direcciones de correo dentro de un 'mailbox', e# imprime el número de veces que aparecen y el nombre junto a la dirección.## [ MBD Thu Jan 16 04:28:02 CET 2003 ]# Un poquito de html, un poquito de css, un par de chorradas y...
## [ MBD Fri Jan 17 02:37:32 CET 2003 ]# Muchas modificaciones, todo lo de los timestamps. Funciones.# FIXME: MUY lento! 'tabla_top 20' tarda 1m10s !!# posibles causas: la conversión a timestamp por medio de date en el bucle# de tabla_top(). El uso de read en el mismo bucle.## [ MBD Fri Jan 17 15:41:58 CET 2003 ]# Aún más modificaciones. Los arrays 'a_froms' y 'a_tstamps' evitan el uso de# 'read' en el bucle de tabla_top (que era lo que ralentizaba todo). Ahora# termina en 5 segundos. ¿Habrá problemas con arrays muy grandes? De momento# 'memusage' indica cerca de 390k empleados.# En teoría no hay problema de memoria, pero recuerdo haber leído algo de que# el entorno de una shell era reducido. ¿?DEBUG=0MBOX=~/Mail/.listas.directory/suse-linux-sMAX=20 #valor por defecto.#HEAD=html/head.html#FOOT=html/foot.htmlHEAD=~/work/bash/rank/html/head2.htmlFOOT=~/work/bash/rank/html/foot2.html# Ficheros temporales. f_res guarda los resultados.# TODO: podría usar otro array sin problemas.f_tmp=$(mktemp /tmp/rank.sh.XXXXXX)f_uniq=$(mktemp /tmp/rank.sh.XXXXXX)# Arrays.declare -a a_fromsdeclare -a a_tstamps################################################################################ Esto borra los fichero temporales al finalizar o pulsar Control-C,# por si nos aburrimos durante el proceso.# $FUNCNAME y $SECONDS son variables especiales de bash.###############################################################################function trap_exit (){echo -n "$FUNCNAME(): Borrando ficheros temporales... " 1>&2rm -f $f_tmprm -f $f_uniqecho "Saliendo." 1>&2echo "Tiempo de ejecución: $SECONDS segundos." 1>&2logger -p user.info "$0: terminado ($SECONDS s)"exit 0}################################################################################ Depurar en bash es muy complicado, mejor algunos mensajes informativos.
###############################################################################function debug (){if [ "$DEBUG" == "1" ]thenecho $1 1>&2elsereturnfi}################################################################################ Modifica las direcciones de correo para que no puedan ser capturadas por# bots de spammers.###############################################################################function antispam (){echo $1 | sed -e 's/@/ AT /g' -e 's/\./ DOT /g'}################################################################################ Construye los arrays $a_froms y $a_tstamps (remitentes y fechas de todos los# mensajes)# NOTA: a_froms no tiene por qué ser un array, pero lo dejo así por claridad# (podría ser simplente una gran cadena)###############################################################################function init_arrays (){cat /dev/null > $f_tmp# 1. Buscamos las líneas From_ .# 2. Esto lo agrega la lista de suse.# 3. Nos quedamos sólo con las direcciones.# 4. Algunas tienen comillas, las quitamos.# 5. Todo en minúsculas.a_froms=($(grep "^From .*$" $MBOX |\grep -v "suse-linux-s-return-.*" |\cut -d" " -f2-2 |\tr -d '"' |\tr [:upper:] [:lower:]))# Listamos las fechas de los mensajesgrep "^From .*$" $MBOX |\grep -v "suse-linux-s-return-.*" |\cut -d" " -f3- > $f_tmp# Esto convierte la fecha a un timestamp.# idiota de mí, me dí cuenta después de escribir 'tstamp.c' para# lo mismo.# Formato: Thu Jan 17 00:21:55 2003# a_tstamps es declarado como un array gracias a los paréntesis.a_tstamps=( $(date -f"$f_tmp" +"%s") )debug "$FUNCNAME(): fin."}################################################################################ Computa el número de gente que escribió desde una fecha determinada y# lo escribe en el fichero temporal $f_uniq.## - $1: Máximo de direcciones escritas al fichero. (def: todas)# - $2: Si está presente, timestamp con la fecha inicial deseada# (def: 0 ==> desde el principio)## Escribe el total de mensajes procesados y el número que cumple el requisito# indicado, en la salida estándar.# TODO: (sencillo) incluir un tercer parámetro con otra fecha para poder# escoger rangos.###############################################################################function ultimos (){local tmplocal maxlocal cnt=0local cnt2=0# Borramos los ficheros con los que vamos a trabajarcat /dev/null > $f_tmpcat /dev/null > $f_uniq# Forma alternativa de hacer lo de abajo, pero bastante menos clara.# max=${1-$MAX}# cmp=${2-"0"}if [ -n "$1" ]; then max=$1; else max=$MAX; fiif [ -n "$2" ]; then cmp=$2; else cmp=0; fidebug "$FUNCNAME(): buscando max=$max, desde $cmp"# Preparamos el fichero con el formato de línea "timestamp direccion"tmp=${#a_froms[*]} # número de elementos en el arrayfor (( cnt=0; cnt < tmp; cnt++))do# la construcción (( )) evalúa expresiones con sintáxis casi de Cif (( a_tstamps[cnt] >= cmp ))thenecho "${a_tstamps[$cnt]} ${a_froms[$cnt]}" >> $f_tmplet cnt2++fidone# a.k.a "return",
echo -n "$cnt $cnt2"debug "$FUNCNAME(): cnt=$cnt, cnt2=$cnt2"# 1. Ordenamos (según el segundo campo)para poder contar.# El orden es inverso para que las fechas empiecen por la más reciente.# 2. Contamos las apariciones y las ponemos.# 3. Ordenamos de mayor a menor número.# 4. Tomamos sólo los $max primeroscat $f_tmp |\sort -r -k2,3|\uniq --count --skip-fields 1 |\sort -r |\head -$max > $f_uniq# Ya no necesitamos estos arrays.unset a_fromsunset a_tstampsdebug "$FUNCNAME(): fin."}function tabla_top (){local linealocal ilocal maillocal remitelocal numlocal cmplocal tstamplocal retdeclare -a ret# Preparamos la lista.init_arrays# Queremos los $1 primeros desde la fecha $2if [ -n "$1" ]; then num=$1; else num=20; fiif [ -n "$2" ]; then cmp=$(date -d"$2" +"%s"); else cmp=0; fidebug "$FUNCNAME(): cmp=$cmp"# ${var-blabla}: si var es nula, usa blabla por defecto.# la primera línea del archivo es una "From", con la fecha.echo "<h3>Las $num personas que más han escrito desde ${2-$(head -1 $MBOX|cut -d" " -f3-)}:</h3>"ret=( $(ultimos $num $cmp) )debug "$FUNCNAME(): ret=${ret[*]}"# ultimos() escribe en la salida estándar el número de mensajes.echo "<p>${ret[0]} mensajes procesados en el archivo. ${ret[1]} cumplen el requisito.</p>"echo "<table class=\"rank\" cellspacing=\"0\">"echo "<tr><th>Posición</th> <th>Nombre</th> <th>Mensajes</th></tr>"# Truquillo para colorear las líneas...i=1# Buscamos los nombres asociados a las direcciones de correo.# Para eso necesitamos las líneas "From:" (nótense los dos puntos)while read lineado# Las líneas tienen el formato: <num> <tstamp> <mail>num=$(echo $linea | cut -d" " -f1)tstamp=$(echo $linea | cut -d" " -f2)mail=$(echo $linea | cut -d" " -f3)# 'mkdate' es un cortísimo programa en C que convierte un timestamp# a una fecha usando la función ctime() ( <time.h> )tstamp=$(~/bin/mkdate "$tstamp")# Tomamos sólo la primera vez que aparece el nombre.# FIXME: MMMMUUUUUYYYYY lento... ¿podría parar grep en el primer# resultado? ¿Construir un fichero una sola vez y luego buscar en él?remite=$(grep "^From:.*${mail}.*" $MBOX | head -1 | cut -d":" -f2)# Nos quedamos con el nombre, sin la dirección.remite=$(echo $remite | tr -d '"' | cut -d"<" -f1)mail=$(antispam $mail)# Si en el From: no había nombre, sólo el email, $remite contiene a lo# sumo un espacio (debido al cut -d"<")if [[ "$remite" < " " ]]thenremite=$mailfi# Coloreamos las líneas (según la hoja de estilo)# pares:rank1. impares:rank2.if (( i % 2 ))thenecho "<tr class=\"rank1\">"elseecho "<tr class=\"rank2\">"ficat <<-EOF<td class="rankpos"><b>$i</b></td><td class="ranknombre"><a href="mailto:$mail">$remite</a><br /><div class="rankfecha">Último mensaje: $tstamp</div></td><td class="ranknum"><b>$num</b></td></tr>EOFlet i++done < $f_uniqecho "<tr><td class=\"rankpie\" colspan=\"3\">Archivo actualizado el $(find $MBOX -printf "%t")</td></tr>"echo "</table>"debug "$FUNCNAME(): fin."}######################### main()
#######################trap trap_exit EXITtrap trap_exit SIGINTcat $HEADdebug "$HEAD incluido."# Dos ejemplos#tabla_top 20#tabla_top 20 "Wed Jan 1 00:00:01 2003"cat $FOOTdebug "$FOOT incluido."- Download this code: rank.sh

