2009-05-07 23 views
9

Estoy escribiendo un script de copia de seguridad de Python y necesito encontrar el archivo más antiguo en un directorio (y sus subdirectorios). También necesito filtrarlo a los archivos * .avi solamente.Encontrar el archivo más antiguo (recursivamente) en un directorio

La secuencia de comandos siempre se ejecutará en una máquina Linux. ¿Hay alguna forma de hacerlo en Python o sería mejor ejecutar algunos comandos de shell?

Por el momento estoy ejecutando df para obtener el espacio libre en una partición en particular, y si hay menos de 5 gigabytes libres, quiero comenzar a eliminar los archivos más antiguos *.avi hasta que se cumpla esa condición.

+1

Espera, ¿cómo estás usando du para obtener espacio libre? Eso solo le dice al uso AFAIK. –

+0

Lo siento, significó dh not du. –

+1

¿Seguro que no es df? : P –

Respuesta

21

Hm. La respuesta de Nadia está más cerca de lo que significa preguntar; Sin embargo, para encontrar el (único) archivo más antiguo de un árbol, intente esto:

import os 
def oldest_file_in_tree(rootfolder, extension=".avi"): 
    return min(
     (os.path.join(dirname, filename) 
     for dirname, dirnames, filenames in os.walk(rootfolder) 
     for filename in filenames 
     if filename.endswith(extension)), 
     key=lambda fn: os.stat(fn).st_mtime) 

Con una pequeña modificación, puede obtener los n archivos más antiguos (similar a la respuesta de Nadia):

import os, heapq 
def oldest_files_in_tree(rootfolder, count=1, extension=".avi"): 
    return heapq.nsmallest(count, 
     (os.path.join(dirname, filename) 
     for dirname, dirnames, filenames in os.walk(rootfolder) 
     for filename in filenames 
     if filename.endswith(extension)), 
     key=lambda fn: os.stat(fn).st_mtime) 

Tenga en cuenta que utilizando el método .endswith permite realizar llamadas como:

oldest_files_in_tree("/home/user", 20, (".avi", ".mov")) 

para seleccionar más de una extensión.

Por último, si quieres ver la lista completa de los archivos, ordenados por fecha de modificación, con el fin de eliminar a todos los que se requiere para liberar espacio, aquí hay un código:

import os 
def files_to_delete(rootfolder, extension=".avi"): 
    return sorted(
     (os.path.join(dirname, filename) 
     for dirname, dirnames, filenames in os.walk(rootfolder) 
     for filename in filenames 
     if filename.endswith(extension)), 
     key=lambda fn: os.stat(fn).st_mtime), 
     reverse=True) 

y observe que el reverse=True trae la archivos más antiguos al final de la lista, de modo que para el siguiente archivo para eliminar, simplemente haga un file_list.pop().

Por cierto, para una solución completa a su problema, ya que se están ejecutando en Linux, donde el os.statvfs está disponible, puede hacerlo:

import os 
def free_space_up_to(free_bytes_required, rootfolder, extension=".avi"): 
    file_list= files_to_delete(rootfolder, extension) 
    while file_list: 
     statv= os.statvfs(rootfolder) 
     if statv.f_bfree*statv.f_bsize >= free_bytes_required: 
      break 
     os.remove(file_list.pop()) 

statvfs.f_bfree son los bloques libres del dispositivo y statvfs.f_bsize es el tamaño de bloque. Tomamos los statvfs rootfolder, así que tenga en cuenta los enlaces simbólicos que apuntan a otros dispositivos, donde podríamos eliminar muchos archivos sin liberar espacio en este dispositivo.

ACTUALIZACIÓN (copiando un comentario de Juan):

Dependiendo de la implementación del sistema operativo y el sistema de archivos, es posible que desee multiplicar por f_bfree f_frsize en lugar de f_bsize. En algunas implementaciones, este último es el tamaño de solicitud de E/S preferido. Por ejemplo, en un sistema FreeBSD 9 que acabo de probar, f_frsize era 4096 y f_bsize era 16384. POSIX dice que los campos de conteo de bloques están "en unidades de f_frsize" (ver http://pubs.opengroup.org/onlinepubs/9699919799//basedefs/sys_statvfs.h.html)

+1

Según el sistema operativo y la implementación del sistema de archivos, es posible que desee multiplicar 'f_bfree' por' f_frsize' en lugar de 'f_bsize'. En algunas implementaciones, este último es el tamaño de solicitud de E/S preferido.Por ejemplo, en un sistema FreeBSD 9 que acabo de probar, 'f_frsize' era 4096 y' f_bsize' era 16384. POSIX dice que los campos de conteo de bloques están "en unidades de f_frsize" - http://pubs.opengroup.org/onlinepubs/ 9699919799 // basedefs/sys_statvfs.h.html – Juan

+0

@Juan muchas gracias! – tzot

2

Consulte el comando de linux find.

Alternativamente, this post juntas ls y tail para eliminar el archivo más antiguo de un directorio. Eso podría hacerse en un bucle mientras no hay suficiente espacio libre.

Para referencia, aquí está el código shell que lo hace (siga el enlace para más alternativas y una discusión):

ls -t -r -1 /path/to/files | head --lines 1 | xargs rm 
13

Para hacerlo en Python, puede utilizar os.walk(path) para recorrer de forma recursiva a través de los archivos, y los atributos st_size y st_mtime de os.stat(filename) para obtener los tamaños de archivo y los tiempos de modificación.

10

Puede utilizar stat y fnmatch módulos juntos para encontrar los archivos

st_mtime refere a la última fecha de modificación. Puede elegir otro valor si quiere

import os, stat, fnmatch 
file_list = [] 
for filename in os.listdir('.'): 
    if fnmatch.fnmatch(filename, '*.avi'): 
     file_list.append((os.stat(filename)[stat.ST_MTIME], filename)) 

Luego puede ordenar la lista por tiempo y borrarla según sea necesario.

file_list.sort(key=lambda a: a[0]) 
0

El os module proporciona las funciones que necesita para obtener los listados de directorios y presentar información en Python. He encontrado que os.walk es especialmente útil para directorios de pie de forma recursiva, y os.stat le proporcionará información detallada (incluido el tiempo de modificación) en cada entrada.

Puede hacer esto más fácil con un simple comando de shell. Si eso funciona mejor para ti o no depende de lo que quieras hacer con los resultados.

7

Creo que la manera más fácil de hacer esto sería usar find junto con ls -t (ordenar los archivos por tiempo).

algo en este sentido debe hacer el truco (borra el archivo más antiguo avi en el directorio especificado)

find/-name "*.avi" | xargs ls -t | tail -n 1 | xargs rm 

paso a paso ....

find/-name "* .avi" - Encontrar todos los archivos avi recursivamente desde el directorio raíz

xargs ls -t - ordenar todos los archivos encontrados por tiempo de modificación, de más reciente al más antiguo.

cola -n 1 - agarrar el último archivo de la lista (la más antigua)

xargs rm - y sacarlo

+0

Menciona ejecutar esto en un bucle. Como 'encontrar' tiende a ser una operación costosa, probablemente sea una mejor idea mantener los resultados de' xargs ls' (quizás en una variable de matriz) y extraer los nombres de archivo uno a la vez. –

+0

Quizás reemplace find con locate y grep? –

2

Aquí hay otra formulación de Python, que es un poco vieja -school comparado con algunos otros, pero es fácil de modificar, y maneja el caso de que no coincidan archivos sin generar una excepción.

import os 

def find_oldest_file(dirname="..", extension=".avi"): 
    oldest_file, oldest_time = None, None 
    for dirpath, dirs, files in os.walk(dirname): 
     for filename in files: 
      file_path = os.path.join(dirpath, filename) 
      file_time = os.stat(file_path).st_mtime 
       if file_path.endswith(extension) and (file_time<oldest_time or oldest_time is None): 
       oldest_file, oldest_time = file_path, file_time 
    return oldest_file, oldest_time 

print find_oldest_file() 
Cuestiones relacionadas