Académique Documents
Professionnel Documents
Culture Documents
MAYORDOMO PYTHON
Mucha gente habla de Python como el sustituto normal de Perl, pero siempre lo vemos en grandes programas. Hoy vamos a demostrar lo fcil que es crear un script potente y sencillo en Python. POR JOS MARA RUIZ
uchas tareas cotidianas pueden automatizarse gracias a los lenguajes script. Siempre se ha dicho que Linux fomenta la creacin de pequeos script que ahorran grandes cantidades de tiempo. Pero con el paso de ste las actividades que se han desarrollado en los sistemas Linux han ido cambiando. Si antes la mayor parte del trabajo consista en gestionar centenares o miles de cuentas de usuarios individuales, actualmente existen cientos de directorios con miles de ficheros que se acumulan en nuestros equipos. Y siempre se desea guardarlos, pero es una tarea complicada, aburrida y montona. Adems hay que pensar! Dnde colocar los ficheros y cmo hacerlo? A continuacin se ver cmo crear un script Python que facilitar esta tarea: se pasar una ruta y una cantidad de espacio (por ejemplo el tamao de un DVD o CDROM, y l coger todos los ficheros de esa ruta y los guardar en directorios de manera que ninguno de ellos sobrepase el tamao que se les di. Uso? Pues el ms evidente, dar una primera idea de cmo poder
guardar toda esa informacin un primer paso para poder almacenar todos esos datos antes de que se tenga que lamentar.
nombre de los ficheros y su tamao. Esto se har con una funcin que adems generar una estructura de datos para almacenar esa informacin. Con la estructura de datos ya en la mano se pasar a la toma de decisiones. De qu manera se van a guardar los datos en los directorios? Para ello se emplear otra funcin, que devolver otra estructura de datos en la que se almacenarn los distintos ficheros clasificados por directorios, con la condicin de que ninguno de esos directorios tenga un nmero tal de ficheros como para sobrepasar el lmite de tamao que se proporci. Esta es la parte dura. Con la nueva estructura de datos en la mano ya slo queda crear los directorios y mover los ficheros a ellos, y listo.
WWW.LINUX- MAGAZINE.ES
Nmero 14
51
DESARROLLO Python
ficheros de la manera ms simple posible? El truco es emplear la recursividad. Una funcin es recursiva cuando se invoca a s misma dentro de su cdigo. Esta tcnica suele provocar dolores de cabeza, pero es ms sencilla de lo que pudiese aparentar en un primer momento. Slo tenemos que seguir dos reglas: que al llamar a la misma funcin de nuevo se le pasen menos datos que a la original. que exista una clusula IF que no ejecute la llamada a la misma funcin. Dicho as suena raro, por eso vamos a verlo en la prctica, ver Listado 1. La funcin recoge dos parmetros, ruta y hash. Como su nombre indica, se emplearn un hash o diccionario para almacenar el nombre y el tamao de los ficheros que se vayan encontrando. A travs de la funcin os.listdir() se consigue una lista con los ficheros y directorios que existen en la ruta que se pase. As que se recorre esa lista, pero las cadenas de la lista slo contienen el nombre de los ficheros y directorios, no
01 def recorre(ruta,hash): 02 entradas = os.listdir(ruta) 03 04 for entrada in entradas: 05 06 ruta_entrada = ruta + "/" + entrada 07 08 if os.path.isfile(ruta_entrada): 09 hash[ruta_entrada] = os.path.getsize(ruta_entrada) 10 elif os.path.isdir(ruta_entrada): 11 recorre(ruta_entrada, hash) 12 else: 13 print "ERROR"
enfrentamos. En caso de que sea un fichero se usa como entrada en el hash su ruta, y el tamao del fichero como valor, que se consigue a travs de la funcin os.path.getsize(). Y qu pasa si es un directorio? Pues que se invoca a recorre() de nuevo! Pero esta vez se pasa la ruta de este subdirectorio. De esta manera, se vuelve a invocar recorre() para cada subdirectorio que se encuentre. Llegar un momento que no se encuentren ms subdirectorios, slo ficheros. Es entonces cuando se detiene la recursin. Imaginemos que se tiene /tmp/ y en su interior el fichero hola.txt y los subdirectorios uno y dos con los ficheros uno.txt y dos.txt respectivamente. Entonces la ejecucin sera como la mostrada en el Listado 2. El resultado final sera que el hash contendra los datos:
{'/tmp/hola.txt' : 123,U '/tmp/uno/uno.txt' : 431,U '/tmp/dos/dos.txt' : 3234 }
su ruta. Por eso se vuelve a generar la ruta usando la variable ruta_entrada para contenerla. Es ahora cuando viene lo difcil. No se sabe la clase de objeto que se tiene entre manos, as que se emplean las funciones os.path.isfile() y os.path.isdir() para ver con qu nos
La clusula else: se ha puesto para poder capturar errores, cosa que no debera pasar nunca. La recursividad, cuando se escapa de la mano, es muy complicada de controlar, pero su buen uso crea cdigo muy sencillo.
El trabajo duro
Es aqu cuando hay que emplear un poco de lgica. Existen muchas maneras de solucionar este problema, pero no tiene mucho sentido emplear las ms eficientes. Es preciso repartir los
ficheros entre contenedores de tamaos iguales especificados por el usuario, o sea, por nosotros mismos. Los mejores algoritmos conseguirn un aprovechamiento ptimo de los contenedores, pero vale la pena? Creo que la mayor parte de la gente dira que no. Por qu? Pues debido a que muchas veces gran parte de los ficheros estn relacionados entre s. No suele ser buena idea meter en un CDROM un fichero grande correspondiente a un captulo de alguna serie y cientos de ficheros pequeos no relacionados, mientras que el segundo captulo de la serie va en otro CDROM. El algoritmo ptimo es el que emplea Programacin Dinmica, una tcnica muy usada para resolver problemas de este tipo. Nosotros emplearemos uno ms sencillo y adecuado, un Algoritmo Voraz. Realmente no tiene mucha historia: comienza por los ficheros ms grandes, e intenta meter tantos como puedas en uno de los contenedores. Cuando no se pueda introducir el siguiente fichero que toque, se salta al siguiente contenedor. De esta manera, los ficheros que se encuentren en el mismo subdirectorio tendern a ir en grupos en los mismos contenedores. La funcin encargada de esta tarea es organiza(), que podemos ver en el Listado 3. Esta funcin hace uso de una caracterstica que no se suele encontrar en otros lenguajes, las funciones Lambda. Una funcin Lambda es una funcin annima, sin nombre. Se puede crear en cualquier momento, y lo que es ms importante, almacenarlas en variables. Como estn asociadas a una variable pueden ser pasadas como parmetros en las funciones. Y la funcin sort() admite 2 funciones como parmetro, de las cuales slo se hace uso de la primera. Por qu se necesita pasar una funcin Lambda? sort() ordena listas, pero la estructura de datos listado no es una lista simple. Es una lista que contiene listas de dos elementos. El primero es el nombre del fichero, y el segundo el tamao. Si se desean ordenar los ficheros por tamao ser necesario decirle a sort() que se fije en el segundo miembro de la lista, que es cada elemento de listado. Y eso es, justamente, lo que hace la funcin Lambda. El primer parmetro de sort() es una funcin que se usa para comparar dos
52
Nmero 14
WWW.LINUX- MAGAZINE.ES
Python DESARROLLO
def recorre(ruta,hash): entradas = os.listdir(ruta) for entrada in entradas: ruta_entrada = ruta + "/" + entrada if os.path.isfile(ruta_entrada): hash[ruta_entrada] = os.path.getsize(ruta_entrada) elif os.path.isdir(ruta_entrada): recorre(ruta_entrada, hash) else: print "ERROR"
016 017 018 019 020 021 def organiza(hash, cantidad): 022 # genera una lista de hash con la cantidad ptima de ficheros para 023 # de manera que cada hash no puede contener ms de esa cantidad. 024 025 listado = hash.items() 026 027 compara = (lambda x,y: cmp(x[1],y[1])) 028 029 listado.sort(compara, reverse=True) 030 contador = 0 031 n = 0 032 directorios = {} 033 034 for entrada in listado: 035 if (contador + entrada[1] <= cantidad): 036 if not n in directorios.keys(): 037 directorios[n] = [] 038 directorios[n].append(entrada) 039 contador += entrada[1] 040 else: 041 n += 1 042 directorios[n] = [] 043 directorios[n].append(entrada) 044 contador = entrada[1] 045 046 return directorios 047 048 def print_directorio(dir, cantidad): 049 050 for llave in dir.keys(): 051 052 cont = 0; 053 print_caja("Directorio " + str(llave))
WWW.LINUX- MAGAZINE.ES
Nmero 14
53
DESARROLLO Python
Figura 1: Estado original de los directorios antes de correr el script. Tenemos un directorio inmenso de 1GB. Figura 2: El script nos parte el directorio original en directorios ms
El siguiente paso pequeos y manejables. consiste en generar una estructura consistente en un dicSe puede ver un ejemplo en el Listado 4. I cionario o hash, donde la llave es un nmero y el contenido una lista de ficheros Listado 4: fichero de ndice y sus tamaos. Este hash representa cada uno de los contenedores que usaremos. 01 <?xml version=1.0> Cuando se llena uno pasamos a crear el 02 <coleccion> siguiente y as hasta quedarnos sin 03 <contenedor id="0"> ficheros.
04
Movimiento de ficheros
Ya slo nos queda recorrer la estructura de datos con genera_dirs(), que genera los directorios y mueve los ficheros a su nueva localizacin. Cabe resaltar la funcin os.path.split() que separa una ruta de fichero en dos, la primera parte de la ruta hasta el nombre del fichero y el nombre del fichero en s mismo. Aqu se usa para generar una nueva ruta. Para ello se emplea la funcin os.renames() que equivale a mover ficheros, pero tambin crea los directorios que estn en la ruta de destino y no existan.
elementos, y que devuelve -1, 0 y 1. Esa funcin ya existe y se llama cmp(). Se cre una funcin Lambda que se aplica a los segundos miembros de las dos listas que admite como parmetro. Veamos su uso:
>>> a = (lambda x,y:U cmp(x[1],y[1]) ... ) >>> a <function <lambda> at 0x81a5dcc> >>> a([0,1],[0,1]) 0 >>> a([0,2],[0,1]) 1 >>>
<fichero>/usr/local/datos/dir0/videos/rubyonrails.mov</fich ero> 05 </contenedor> 06 <contenedor id="1"> 07 <fichero>/usr/local/datos/dir1/ase2001-gfkf.ps</fichero> 08 <fichero>/usr/local/datos/dir1/icse2002-gk.ps</fichero> 09 <fichero>/usr/local/datos/dir1/esop2003-gfkf.ps</fichero> 10 <fichero>/usr/local/datos/dir1/continuations.ps</fichero> 11 <fichero>/usr/local/datos/dir1/esop2001-gkvf.ps</fichero> 12 </contenedor> 13 </coleccion>
Jos Mara Ruiz actualmente est realizando el Proyecto Fin de Carrera de Ingeniera Tcnica en Informtica de Sistemas. Lleva 8 aos usando y desarrollando software libre y, desde hace dos, se est especializando en FreeBSD.
54
Nmero 14
WWW.LINUX- MAGAZINE.ES
EL AUTOR