Un pequeño script Util
-
Upload
aprender-libre -
Category
Documents
-
view
223 -
download
0
Transcript of Un pequeño script Util
-
7/31/2019 Un pequeo script Util
1/4
Python DESARROLLO
51Nmero 14W W W . L I NUX- M A GA Z I NE . E S
guardar toda esa informacin un
primer paso para poder almacenar
todos esos datos antes de que se tenga
que lamentar.
Diseo del programaSer preciso pensar ahora en lo que se
va a necesitar. Como se dispone de una
ruta y de una cantidad de espacio
expresado de alguna manera (digamos
Megabytes), lo primero que habr que
hacer es acceder a dicha ruta.
Para ello se harn uso de las fun-ciones de las libreras os y os.path. Una
vez en la ruta deben recorrerse todos
los directorios y subdirectorios que se
encuentran en ella, registrando el
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 deci-
siones. 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 deesos 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 estruc-
tura de datos en la
mano ya slo queda
crear los directorios y
mover los ficheros a
ellos, y listo.
Recoleccin de
los datosCmo se pueden
recorrer todos los sub-
directorios de
un rbol
de
Muchas tareas cotidianas
pueden automatizarse gracias
a los lenguajes script. Siempre
se ha dicho que Linux fomenta la
creacin de pequeos script que ahor-
ran 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 milesde ficheros que se acumulan en nues-
tros 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 canti-
dad de espacio (por ejemplo
el tamao de un DVD oCDROM, 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
Un script pequeo y til
MAYORDOMOPYTHONMucha 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
-
7/31/2019 Un pequeo script Util
2/4
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. Esentonces cuando se detiene la recur-
sin.
Imaginemos que se tiene /tmp/ y en
su interior el fichero hola.txty los sub-
directorios 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 }
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 duroEs aqu cuando hay que emplear un
poco de lgica. Existen muchas ma-
neras 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 con-
tenedores, pero vale la pena? Creoque 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 iren grupos en los mismos contenedores.
La funcin encargada de esta tarea es
organiza(), que podemos ver en el Lis-
tado 3. Esta funcin hace uso de una ca-
racterstica 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() admite2 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, justa-
mente, lo que hace la funcin Lambda.
El primer parmetro de sort() es una
funcin que se usa para comparar dos
ficheros de la manera ms simple posi-
ble? 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 loque 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 eje-
cute 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 con-
sigue una lista con los ficheros y direc-
torios 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
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
DESARROLLO Python
52 Nmero 14 W W W . L I NUX- M A GA Z I NE . E S
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"
Listado 1: funcinrecorre
- se ejecuta recorrido("/tmp",{})
- se procesa hola.txt
- se ejecutarecorrido("/tmp/uno",{'/tmp/hola.txt' :
123})
- se procesa uno.txt
- se vuelve
- se ejecuta
recorrido("/tmp/dos",{'/tmp/hola.txt' :
123, '/tmp/uno/uno.txt' : 431})
- se procesa dos.txt
- se vuelve
- se vuelve
Listado 2: ejecucinrecursiva
-
7/31/2019 Un pequeo script Util
3/4
Python DESARROLLO
53Nmero 14W W W . L I NUX- M A GA Z I NE . E S
001 #!/usr/local/bin/python
002
003 import os
004 import os.path005
006
007 def recorre(ruta,hash):
008 entradas = os.listdir(ruta)
009
010 for entrada in entradas:
011
012 ruta_entrada = ruta + "/" + entrada
013
014 if os.path.isfile(ruta_entrada):
015 hash[ruta_entrada] =
os.path.getsize(ruta_entrada)016 elif os.path.isdir(ruta_entrada):
017 recorre(ruta_entrada, hash)
018 else:
019 print "ERROR"
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()
026027 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]
-
7/31/2019 Un pequeo script Util
4/4
El siguiente paso
consiste en generar
una estructura consistente en un dic-
cionario o hash, donde la llave es un
nmero y el contenido una lista de ficheros
y sus tamaos. Este hash representa cada
uno de los contenedores que usaremos.
Cuando se llena uno pasamos a crear el
siguiente y as hasta quedarnos sin
ficheros.
Movimiento de ficherosYa 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 funcinos.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.
La guinda del pastelComo somos grandes hackers y estamos a
la ltima se ha incorporado al programauna ltima funcin. La funcin
vuelca_indice() genera un fichero XML con
el ndice de contenedores y ficheros. El
objetivo es que quien quiera crear un pro-
grama que grabe estos contenidos podr
facilitarse mucho la tarea leyendo este
ndice en lugar de tener que escanear los
directorios.
Una de las grandes ventajas del XML es
que resulta muy sencillo generarlo. La
estructura de nuestro fichero es bien sim-
ple:
Coleccin
Contenedor
Fichero
Se puede ver un ejemplo en el Listado 4.I
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
>>> a([0,1],[0,1])
0
>>> a([0,2],[0,1])
1
>>>
es importante fijarse como a pasa a con-
vertirse en un funcin como cualquier
otra.
DESARROLLO Python
54 Nmero 14 W W W . L I NUX- M A GA Z I NE . E S
Jos Mara Ruiz actualmente
est realizando el Proyecto Fin
de Carrera de Ingeniera Tcnica
en Informtica de Sistemas.
Lleva 8 aos usando y desarro-
llando software libre y, desdehace dos, se est especializando
en FreeBSD.
EL
AUTOR
01
02
03
04
/usr/local/datos/dir-
0/videos/rubyonrails.mov
05
06
07
/usr/local/datos/dir-
1/ase2001-gfkf.ps
08
/usr/local/datos/dir-
1/icse2002-gk.ps
09
/usr/local/datos/dir-
1/esop2003-gfkf.ps
10
/usr/local/datos/dir-
1/continuations.ps
11/usr/local/datos/dir-
1/esop2001-gkvf.ps
12
13
Listado 4: fichero de ndice
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
pequeos y manejables.
103 fich.close()104
105 if __name__ == '__main__':
106
107 hash = {}
108 ruta = "/usr/local/datos"
109 cantidad = 60000000 #en
bytes
110
111 recorre(ruta,hash)
112
113 d =
organiza(hash,cantidad)
114 print_directorio(d,
cantidad)
115
116 genera_dirs(ruta,d)
117
118 vuelca_indice(ruta,d)
Listado 3: cdigo delprograma (continuacin)