2009-05-07 10 views
9

Sto scrivendo uno script di backup Python e ho bisogno di trovare il file più vecchio in una directory (e le sue sottodirectory). Devo anche filtrarlo solo nei file * .avi.Trova il file più vecchio (in modo ricorsivo) in una directory

Lo script sarà sempre in esecuzione su una macchina Linux. C'è un modo per farlo in Python o fare in modo che alcuni comandi di shell siano migliori?

Al momento sto correndo df per ottenere lo spazio libero in una partizione particolare e, se non v'è meno di 5 gigabyte di spazio libero, voglio iniziare a cancellare i vecchi file *.avi fino a quel condizione è soddisfatta.

+1

Aspettare come si usa du per ottenere lo spazio libero? Questo dice solo l'uso di AFAIK. –

+0

Mi spiace, intendevo dh non du. –

+1

Sicuro che non sia df? : P –

risposta

21

Hm. La risposta di Nadia è più vicina a ciò che voi significava chiedere; tuttavia, per trovare il (singolo) più vecchio file in un albero, provate questo:

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 piccola modifica, è possibile ottenere i n file più vecchi (simile alla risposta di 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) 

si noti che utilizzando il metodo .endswith permette di chiamare come:

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

per selezionare più di un'estensione.

Infine, se si desidera che l'elenco completo dei file, in ordine di data di modifica, al fine di eliminare il maggior numero richiesto per lo spazio libero, ecco qualche codice:

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) 

e notare che il reverse=True porta il file più vecchi alla fine dell'elenco, in modo che per il prossimo file da eliminare, basta fare un file_list.pop().

Tra l'altro, per una soluzione completa al problema in questione, dal momento che è in esecuzione su Linux, dove il os.statvfs è disponibile, si può fare:

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 sono i blocchi liberi del dispositivo e statvfs.f_bsize è il misura del blocco. Prendiamo le statvfs rootfolder, quindi fai attenzione ai collegamenti simbolici che puntano ad altri dispositivi, dove potremmo eliminare molti file senza liberare spazio in questo dispositivo.

UPDATE (la copia di un commento di Juan):

seconda dell'implementazione sistema operativo e file system, si consiglia di moltiplicare f_bfree da f_frsize piuttosto che f_bsize. In alcune implementazioni, quest'ultima è la dimensione richiesta di I/O preferita. Ad esempio, su un sistema FreeBSD 9 Ho appena testato, f_frsize erano 4096 e f_bsize era 16384. POSIX dice che i campi di conteggio del blocco sono "in unità di f_frsize" (vedi http://pubs.opengroup.org/onlinepubs/9699919799//basedefs/sys_statvfs.h.html)

+1

A seconda dell'implementazione del sistema operativo e del filesystem, è possibile moltiplicare 'f_bfree' di' f_frsize' piuttosto che 'f_bsize'. In alcune implementazioni, quest'ultima è la dimensione richiesta di I/O preferita.Ad esempio, su un sistema FreeBSD 9 che ho appena testato, 'f_frsize' era 4096 e' f_bsize' era 16384. POSIX dice che i campi di conteggio dei blocchi sono "in unità di f_frsize" - http://pubs.opengroup.org/onlinepubs/ 9699919799 // basedefs/sys_statvfs.h.html – Juan

+0

@Juan grazie mille! – tzot

2

Controllare il comando linux find.

In alternativa, this post pipe insieme ls e tail per eliminare il file più vecchio in una directory. Quello potrebbe essere fatto in un ciclo mentre non c'è abbastanza spazio libero.

Per riferimento, ecco il codice shell che lo fa (Segui il link per più alternative e una discussione):

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

Per farlo in Python, è possibile utilizzare os.walk(path) per iterare ricorsivamente sui file, e gli attributi st_size e st_mtime di os.stat(filename) per ottenere le dimensioni del file e i tempi di modifica.

10

È possibile utilizzare stat e fnmatch moduli insieme per trovare i file

st_mtime Riferirsi ai dell'ultima modifica. È possibile scegliere un altro valore se si desidera

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

Quindi è possibile ordinare l'elenco per tempo ed eliminare in base ad esso.

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

Il os module fornisce le funzioni di cui avete bisogno per ottenere elenchi di directory e file info in Python. Ho trovato os.walk particolarmente utile per le directory di camminata ricorsive e os.stat fornirà informazioni dettagliate (incluso il tempo di modifica) su ogni voce.

Potrebbe essere possibile farlo con un semplice comando di shell. Che funzioni o meno per te dipende da cosa vuoi fare con i risultati.

7

Penso che il modo più semplice per farlo sarebbe quello di utilizzare find insieme a ls -t (ordina i file in base al tempo).

qualcosa in questo senso dovrebbe fare il trucco (cancella il più vecchio file avi nella directory specificata)

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

passo dopo passo ....

find/-name "* .avi" - trova tutti i file avi in ​​modo ricorsivo a partire dalla directory radice

xargs ls -t - ordina tutti i file trovati per ora di modifica, dal più recente al più vecchio.

tail -n 1 - afferrare l'ultimo file della lista (più vecchia)

xargs rm - e rimuoverlo

+0

Egli menziona l'esecuzione di questo in un ciclo. Poiché 'find' tende ad essere un'operazione costosa, è probabilmente una buona idea mantenere i risultati di' xargs ls' attorno (magari in una variabile di array) e tirare i nomi dei file da una alla volta. –

+0

Forse sostituire find con locate e grep? –

2

Ecco un'altra formulazione Python, che un po 'vecchio -school rispetto ad altri, ma è facile da modificare e gestisce il caso di nessun file corrispondente senza generare un'eccezione.

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() 
Problemi correlati