2009-05-07 13 views
9

J'écris un script de sauvegarde Python et j'ai besoin de trouver le fichier le plus ancien dans un répertoire (et ses sous-répertoires). Je dois également filtrer vers les fichiers * .avi uniquement.Trouver le fichier le plus ancien (récursivement) dans un répertoire

Le script sera toujours exécuté sur une machine Linux. Y a-t-il un moyen de le faire en Python ou est-ce que l'exécution de certaines commandes shell serait meilleure?

En ce moment je suis en cours d'exécution df pour obtenir l'espace libre sur une partition particulière, et s'il y a moins de 5 gigaoctets libre, je veux commencer à supprimer les plus anciens fichiers *.avi jusqu'à cette condition est remplie.

+1

Attendez-vous comment utilisez-vous pour obtenir de l'espace libre? Cela ne dit que l'usage AFAIK. –

+0

Désolé, cela signifiait dh pas du. –

+1

Vous êtes sûr que ce n'est pas df? : P –

Répondre

21

Hm. La réponse de Nadia est plus proche de ce que vous appelez ; cependant, pour trouver le (seul) fichier le plus ancien dans un arbre, essayez ceci:

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) 

Avec une petite modification, vous pouvez obtenir les fichiers les plus anciens n (similaire à la réponse 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) 

Notez que l'utilisation de la méthode .endswith permet d'appeler comme:

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

pour sélectionner plus d'une extension.

Enfin, si vous voulez la liste complète des fichiers, commandés par date de modification, afin de supprimer autant que nécessaire à l'espace libre, voici un code:

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) 

et notez que le reverse=True apporte la fichiers les plus anciens à la fin de la liste, de sorte que pour le prochain fichier à supprimer, vous faites juste un file_list.pop().

Par ailleurs, une solution complète à votre question, puisque vous exécutez sur Linux, où le os.statvfs est disponible, vous pouvez le faire:

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 sont les périphériques blocs libres et statvfs.f_bsize est la taille de bloc. Nous prenons le rootfolder statvfs, donc attention aux liens symboliques pointant vers d'autres périphériques, où nous pourrions supprimer beaucoup de fichiers sans réellement libérer de l'espace dans cet appareil.

UPDATE (la copie d'un commentaire par Juan):

En fonction du système d'exploitation et la mise en œuvre le système de fichiers, vous voudrez peut-être multiplier par f_bfree f_frsize plutôt que f_bsize. Dans certaines implémentations, cette dernière est la taille de requête d'E/S préférée. Par exemple, sur un système FreeBSD 9 Je viens de tester, f_frsize était 4096 et f_bsize était 16384. Posix dit les champs de comptage de blocs sont « en unités de f_frsize » (voir http://pubs.opengroup.org/onlinepubs/9699919799//basedefs/sys_statvfs.h.html)

+1

En fonction de l'OS et de l'implémentation du système de fichiers, vous pouvez multiplier 'f_bfree' par' f_frsize' plutôt que par 'f_bsize'. Dans certaines implémentations, cette dernière est la taille de requête d'E/S préférée.Par exemple, sur un système FreeBSD 9 que je viens de tester, 'f_frsize' était 4096 et' f_bsize' était 16384. POSIX dit que les champs de compte de bloc sont "en unités de f_frsize" - http://pubs.opengroup.org/onlinepubs/ 9699919799 // basedefs/sys_statvfs.h.html – Juan

+0

@Juan merci beaucoup! – tzot

2

Vérifiez la commande linux find.

Alternativement, this post les tuyaux ensemble ls et queue pour supprimer le fichier le plus ancien dans un répertoire. Cela pourrait être fait dans une boucle alors qu'il n'y a pas assez d'espace libre.

Pour référence, voici le code shell qui le fait (cliquez sur le lien pour plus d'alternatives et une discussion):

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

Pour le faire en Python, vous pouvez utiliser os.walk(path) pour itérer récursivement sur les fichiers, et les attributs st_size et st_mtime de os.stat(filename) pour obtenir les tailles de fichier et les heures de modification.

10

Vous pouvez utiliser stat et fnmatch modules ensemble pour trouver les fichiers

st_mtime refere à la dernière date de modification. Vous pouvez choisir une autre valeur si vous le souhaitez

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

Ensuite, vous pouvez trier la liste en fonction de l'heure et la supprimer.

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

Le os module fournit les fonctions dont vous avez besoin pour obtenir des listes de répertoires et de fichiers d'informations en Python. J'ai trouvé os.walk particulièrement utile pour parcourir les répertoires récursivement, et os.stat vous donnera des informations détaillées (y compris les temps de modification) sur chaque entrée.

Vous pouvez peut-être le faire plus facilement avec une simple commande shell. Que cela fonctionne mieux pour vous ou non dépend de ce que vous voulez faire avec les résultats.

7

Je pense que la façon la plus simple de faire cela serait d'utiliser find avec ls -t (trier les fichiers par temps).

quelque chose le long de ces lignes devrait faire l'affaire (supprime le plus ancien fichier avi sous répertoire spécifié)

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

étape par étape ....

find/-name "* .avi" - trouver tous les fichiers avi récursivement à partir du répertoire racine

xargs ls -t - trier tous les fichiers trouvés par heure de modification, du plus récent au plus ancien.

tail -n 1 - saisir le dernier fichier dans la liste (la plus ancienne)

xargs rm - et retirez-le

+0

Il mentionne cela en boucle. Comme 'find' a tendance à être une opération coûteuse, c'est probablement une meilleure idée de conserver les résultats de' xargs ls' (peut-être dans une variable array) et d'en extraire les noms de fichiers un à la fois. –

+0

Peut-être remplacer trouver avec locate et grep? –

2

Voici une autre formulation Python, qui un peu vieux -école par rapport à d'autres, mais est facile à modifier, et gère le cas d'aucun fichier correspondant sans lever une exception.

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() 
Questions connexes