[ACCEPTED]-Find the oldest file (recursively) in a directory-file-io

Accepted answer
Score: 26

Hm. Nadia's answer is closer to what you 28 meant to ask; however, for finding the (single) oldest 27 file in a tree, try this:

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)

With a little modification, you 26 can get the n oldest files (similar to Nadia's 25 answer):

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)

Note that using the .endswith method allows 24 calls as:

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

to select more than one extension.

Finally, should 23 you want the complete list of files, ordered 22 by modification time, in order to delete 21 as many as required to free space, here's 20 some 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)

and note that the reverse=True brings the 19 oldest files at the end of the list, so 18 that for the next file to delete, you just 17 do a file_list.pop().

By the way, for a complete solution 16 to your issue, since you are running on 15 Linux, where the os.statvfs is available, you can 14 do:

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 are the device free blocks and statvfs.f_bsize is the 13 block size. We take the rootfolder statvfs, so mind 12 any symbolic links pointing to other devices, where 11 we could delete many files without actually 10 freeing up space in this device.

UPDATE (copying 9 a comment by Juan):

Depending on the OS and 8 filesystem implementation, you may want 7 to multiply f_bfree by f_frsize rather than 6 f_bsize. In some implementations, the latter 5 is the preferred I/O request size. For example, on 4 a FreeBSD 9 system I just tested, f_frsize 3 was 4096 and f_bsize was 16384. POSIX says 2 the block count fields are "in units 1 of f_frsize" ( see http://pubs.opengroup.org/onlinepubs/9699919799//basedefs/sys_statvfs.h.html )

Score: 14

To do it in Python, you can use os.walk(path) to iterate 3 recursively over the files, and the st_size and 2 st_mtime attributes of os.stat(filename) to get the file sizes and 1 modification times.

Score: 11

You can use stat and fnmatch modules together to find 4 the files

ST_MTIME refere to the last modification 3 time. You can choose another value if you 2 want

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

Then you can order the list by time 1 and delete according to it.

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

I think the easiest way to do this would 9 be to use find along with ls -t (sort files 8 by time).

something along these lines should 7 do the trick (deletes oldest avi file under 6 specified directory)

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

step by step....

find / -name "*.avi" - find 5 all avi files recursively starting at the 4 root directory

xargs ls -t - sort all files found by 3 modification time, from newest to oldest.

tail -n 1 - grab 2 the last file in the list (oldest)

xargs rm - and 1 remove it

Score: 4

Here's another Python formulation, which 4 a bit old-school compared to some others, but 3 is easy to modify, and handles the case 2 of no matching files without raising an 1 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()
Score: 2

Check out the linux command find.

Alternatively, this post pipes 6 together ls and tail to delete the oldest 5 file in a directory. That could be done 4 in a loop while there isn't enough free 3 space.

For reference, here's the shell code 2 that does it (follow the link for more alternatives 1 and a discussion):

ls -t -r -1 /path/to/files | head --lines 1 | xargs rm
Score: 0

The os module provides the functions that you need 9 to get directory listings and file info 8 in Python. I've found os.walk to be especially 7 useful for walking directories recursively, and 6 os.stat will give you detailed info (including 5 modification time) on each entry.

You may 4 be able to do this easier with a simple 3 shell command. Whether that works better 2 for you or not depends on what you want 1 to do with the results.

Score: 0

Using standard library's pathlib:

from pathlib import Path

def creation_time(path: Path):
    return path.stat().st_ctime

working_dir = Path()
avi_files = working_dir.glob("**/*.avi")
sorted_avi_files = sorted(avi_files, key=creation_time)
print(sorted_avi_files[0])

Or if you like 1 one-liners:

min(Path().glob("**/*.avi"), key=lambda p: p.stat().st_ctime)

More Related questions