Category Archives: Python

Eligiendo Elementos de una Lista

Imaginemos que queremos personalizar el fichero de entrada al sistema /etc/motd con frases de consejos para los usuarios que se conecten, pero que no queremos usar el programa fortune porque nos hemos cansado de la falsa aleatoriedad de los mensajes y que siempre acaban saliendo los mismos. Vamos a ver como solucionar esto con nuestro propio sistema.

Vamos a escribir un pequeño script de Python que elegirá una de las frases de un fichero y la escribirá en el /etc/motd, borrara esa frase del fichero original para no tener que verla nunca más.

La cabezera estandard.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

Importamos los módulos necesarios

import os,random

Definimos los fichero de entrada y salida así como la lista que almacenara las frases.

input = 'input.txt'
output = 'output.txt'
lista = []

Empezamos a recorrer el script y lo primero que hacemos es comprobar que el fichero no esta vacío. Si lo está, damos un error y salimos. En caso contrario rellenamos la lista con las frases.

f = open (input, "r")
if os.stat(input)[6] == 0:
  print "Fichero vacío"
  exit (2)
else:
  for line in f:
    lista.append (line.strip())
f.close

Elegimos un elemento de la lista al azar y lo escribimos en el fichero de salida.

total = len (lista)
elegida = random.randint(0,total - 1) # Python como C y casi todos cuenta desde 0
f = open (output, "w")
f.write ("%s\n" % lista[elegida])
f.close

Eliminamos ese elemento para que no vuelva a salir.

lista.remove (lista[elegida])

Guardamos la nueva lista

f = open (input, "w")
for item in lista:
  f.write ("%s\n" % item)
f.close

Para ejecutarlo, simplemente le damos un nombre y permiso de ejecución. Conforme alimentemos el fichero de entrada iran apareciendo frases distintas en el fichero de salida. Si el fichero de entrada se vacia, el de salida guardará la última frase que se introdujo. Esto último se puede cambiar.

Como bien comenta nuestro amigo el Informático de Guardia, el problema puede ser resulto en un sola línea haciendo simplemente:

cat input.txt | sort -R | head -n 1 > output.txt

Aunque está solución si saca frases repetidas al no ir eliminado las que ya han sido elegidas, además si el fichero de entrada está vacío el de salida se vacía. Pero sólo necesitamos poner un par de líneas más.

Estructuras de Datos en Python

¿Recuerdas los tiempos en que definías un struct en lenguaje C, lo tenias que guardar en un fichero binario y luego recuperarlo haciendo un bucle leyendo cada struct? Pero claro, para ello debías conocer como estaba definida la estructura, el programador original debía proporcionar la especificación…

Afortunadamente, los tiempos han cambiado, en Python disponemos del módulo Pickle que se encargará de esa tediosa labor de guardar y recuperar datos de un fichero.

Read more »

Notificaciones en Bash y Python

Aunque mis conocimientos se basan en estar muchas horas en la consola, escribiendo crípticos comandos, que sólo yo y Jon Maddog conocemos, utilizo un gestor de ventanas para navegar por la web, ver vídeos, escuchar música y cosas así. Puedo unir la potencia de la línea de comandos con el atractivo visual de un entorno de ventanas. Gracias al comando notify-send podemos enviar mensajes a la bandeja del sistema como por ejemplo:

Read more »

Notificando Nuevas Series Compartidas

Como todo buen friki que se precie, sigo un montón de series, tantas que mis usuarios se pierden cada día buscando las novedades en la carpeta compartida por NFS que tengo en mi red. El directorio compartido es /mnt/almacen/Media, un Seagate Barracuda de 1TB, de ahí cuelgan las series cada una en su directorio, muy bien ordenado.

Pues bien, apoyandome en el demonio Incron, he creado un sistema para notificar por correo electrónico a mi lusers cada novedad. El problema es que incron no implementa todavía la recursividad sobre subdirectorios, así que hay que supervisar cada uno por separado.

Este es el contenido:

bigBangTheory            Dollhouse           House          Mental               Numbers         RobinsonCrusoe    TheBeast
Bones                    EntreFantasmas      Hung           MentesCriminales     Olvidados       RomaCriminal      TheITCrowd
cocheFantastico          Eureka              incoming       Mercy                PadreDeFamilia  SamanthaWho       TheListener
ComoConociAVuestraMadre  EverybodyHateChris  kyle           Mienteme             pelisVistas     scrubs            TorchWood
csi                      FlashForward        LaPeceraDeEva  Monk                 Psych           siesta            Trauma
Deadwood                 Greek               Life           MP3s                 Reaper          Simpson           TrueBlood
Dexter                   Heroes              LifeOnMars     MujeresDesesperadas  Ritchies        StargateAtlantis  videosChicos
Docus                    Historicas          meLlamoEarl    MyOwnWorstEnemy      RobinHood       supernatural      Weeds

Escribir una línea de incrontab para cada uno es algo que no estaba dispuesto a hacer, así que se recurre a find y awk.

find /mnt/almacen/Media/ -maxdepth 1 | awk '{ print $1 " IN_MOVED_TO /home/nordri/bin/incron/nuevoMedio.py $@ $# " }'

Este comando, que debemos redireccionar a un fichero, nos mostrará esto:

[...]
/mnt/almacen/Media/Mienteme IN_MOVED_TO /home/nordri/bin/incron/nuevoMedio.py $@ $#
[...]

Que creara un monitor para el directorio, en caso que algún fichero sea movido dentro del directorio, ejecutará el comando nuevoMedio.py con los parametros $@ y $# que son la ruta completa y el nombre del archivo.

Existen, también dos detalles, uno es el directorio históricas donde guardo las series finalizadas, canceladas y discontinuadas. Esto es porque me gusta saber que series he seguido. El otro detalle, es supervisar la raíz de Media para avisar en caso que se añada una nueva serie. Las líneas que hay que añadir al incrontab son las siguientes.

/mnt/almacen/Media/Historicas IN_MOVED_TO /home/nordri/bin/incron/nuevoHistorico.py $# 
/mnt/almacen/Media IN_CREATE /home/nordri/bin/incron/nuevoDirMedio.py $#

Bien, pasemos a la programación, todos los scripts son en el básico Python que manejo, pero que resulta útil para estas cuestiones. El script nuevoMedio.py

import avisoPorEmail
import sys
def getCategoria(t):
  c = t.split("/")
  l = len(c)
  return c[l-1]
categoria  = getCategoria(sys.argv[1])
email = open("/tmp/email", "w")
email.write("En la categoria : " + categoria + "\n\nSe ha compartido un nuevo archivo multimedia: \n\n" + sys.argv[2] + "\n\n enjoy ;)")
email.close()
asunto = "Nuevo archivo multimedia"
direcciones = [ #direcciones# ]
avisoPorEmail.mail('/tmp/email', asunto, direcciones, 'txt')

Simplemente, recibe la ruta del directorio compartido, elimina las barras y nos quedamos el nombre de la serie, que será la categoría, el segundo parámetro es el nombre del archivo, que lleva consigo el episodio, y todo lo que los uploaders le añaden para autopromocionarse. Se escribe la cadena en un archivo y se manda por correo a las direcciones que se le indican en el array. Hay que avisar a los usuarios que miren en spam por si acaso. A mi me pasa.

Los scripts de nuevoDirMedio.py y nuevoHistorico.py son muy similares.

import avisoPorEmail
import sys
import os
categoria  = sys.argv[1]
email = open("/tmp/email", "w")
email.write("Se está siguiendo una nueva serie : " + categoria + "\n\n enjoy ;)")
email.close()
asunto = "Nueva Serie"
direcciones = [ #direcciones# ]
# Dar de alta el nuevo directorio en incron
os.system('/home/nordri/bin/incron/nuevoDirVigilado.sh ' + categoria)
avisoPorEmail.mail('/tmp/email', asunto, direcciones, 'txt')

Prácticamente igual, la peculiaridad aquí está en que si creamos un nuevo directorio, debemos dar de alta el monitor para incron. Para ello utilizo el siguiente script.

echo "/mnt/almacen/Media/$1 IN_MOVED_TO /home/nordri/bin/incron/nuevoMedio.py \$@ \$#" > /tmp/tareaIncron.txt
incrontab -l >> /tmp/tareaIncron.txt
incrontab /tmp/tareaIncron.txt

Simple, no? Se escribe la tarea en un archivo auxiliar, se anexa el contenido del incrontab actual y se recarga usando el archivo auxiliar como base.

El último es para cuando una serie se pasa al histórico, así,

import avisoPorEmail
import sys
def getCategoria(t):
  c = t.split("/")
  l = len(c)
  return c[l-1]
categoria  = getCategoria(sys.argv[1])
email = open("/tmp/email", "w")
email.write("La serie : " + categoria + "\n\nHa sido cancelada o discontinuada. Perdone las molestias")
email.close()
asunto = "Serie abandonada"
direcciones = [ #Direcciones# ]
avisoPorEmail.mail('/tmp/email', asunto, direcciones, 'txt')

Pues bien, cada vez que algo se mueva del incoming del Torrent a su directorio se avisará a los usuarios con un correo electrónico.

Cumpleaños

Hoy es mi cumpleaños, me ha llamado mucha gente que se ha acordado, gracias a todos. Para celebrarlo he añadido una nueva funcionalidad al script de las peliculas que tanto ha dado que hablar.

Ahora, cuando se añaden nuevas películas a la lista se ponen en negrita con un letrero parpadeante al lado en rojo que reza "Novedad".

Para lograr tal hazaña, hablé con mi amigo Miguel Angel que me comentó que hiciera un tree y luego un diff al árbol de directorios para ver que había cambiado, nada más lejos, la solución que yo propongo se apoya en la teoría de conjuntos, si tenemos un conjunto A con x elementos y un conjunto B con x + n elementos podemos hacer la operación diferencia para obtener los n elementos que difieren en los conjuntos. Estos n elementos son las novedades, así, mientras se añaden títulos al HTML se diferencian en si están contenidos en la lista de novedades o no. 

Para obtener las listas me he apoyado en otro script Python que hice en su día y que recorre el árbol de directorios, lo he modificado para que en lugar de hacer una copia espejo añada cada elemento a una lista. 

Como siempre, el código fuente aquí

También me he comprado una bici.