Encuentra el archivo más antiguo (recursivamente) en un directorio

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 solo a archivos * .avi.

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

En este 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 *.avi más antiguos hasta que se cumpla esa condición.

Hm La respuesta de Nadia está más cerca de lo que querías preguntar; sin embargo, para encontrar el archivo más antiguo (único) en 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 el uso del método .endswith permite llamadas como:

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

para seleccionar más de una extensión.

Finalmente, si desea obtener la lista completa de archivos, ordenados por hora de modificación, para eliminar tantos como sea necesario para liberar espacio, aquí hay algo de 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 tenga en cuenta que reverse=True trae los archivos más antiguos al final de la lista, de modo que para el siguiente archivo que desea eliminar, simplemente haga un file_list.pop() .

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

 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 del bloque. Tomamos las rootfolder la rootfolder raíz, por lo que rootfolder preocupamos por 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 del 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, esta última 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 recuento de bloques están “en unidades de f_frsize” (consulte http://pubs.opengroup.org/onlinepubs/9699919799/ basedefs / sys_statvfs.h.html )

Para hacerlo en Python, puede usar os.walk(path) para iterar recursivamente sobre los archivos, y los st_size y st_mtime de os.stat(filename) para obtener los tamaños de archivo y los tiempos de modificación.

Puedes usar los módulos stat y fnmatch juntos para encontrar los archivos

ST_MTIME refere a la última hora de modificación. Puedes elegir otro valor si quieres.

 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 eliminar de acuerdo a ella.

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

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

algo en esta línea debería hacer el truco (elimina el archivo avi más antiguo en el directorio especificado)

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

paso a paso….

find / -name “* .avi” – encuentra todos los archivos avi de forma recursiva comenzando en el directorio raíz

xargs ls -t : ordena todos los archivos encontrados por hora de modificación, desde el más reciente al más antiguo.

tail -n 1 – toma el último archivo de la lista (el más antiguo)

xargs rm – y eliminarlo

Aquí hay otra formulación de Python, que es un poco anticuada en comparación con otras, pero es fácil de modificar y maneja el caso de que no haya archivos coincidentes sin presentar 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 

Echa un vistazo a la orden de linux find .

Alternativamente, esta publicación une ls y tail para eliminar el archivo más antiguo en un directorio. Eso podría hacerse en un bucle mientras no haya suficiente espacio libre.

Para referencia, aquí está el código de 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 

El módulo os proporciona las funciones que necesita para obtener listados de directorios e información de archivos en Python. He encontrado que os.walk es especialmente útil para recorrer directorios recursivamente, y os.stat le dará información detallada (incluida la hora de modificación) en cada entrada.

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