un pequeño script util

Upload: aprender-libre

Post on 05-Apr-2018

221 views

Category:

Documents


0 download

TRANSCRIPT

  • 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)