在目录中(递归)查找最旧的文件

12 投票
8 回答
17943 浏览
提问于 2025-04-15 11:27

我正在写一个Python备份脚本,需要找到一个文件夹(以及它里面的子文件夹)中最旧的文件。而且,我只想找后缀是*.avi的文件。

这个脚本会一直在Linux机器上运行。请问有没有办法用Python来实现,还是用一些命令行指令更好呢?

目前我在用df命令来查看某个分区的可用空间,如果可用空间少于5GB,我想开始删除最旧的*.avi文件,直到空间满足要求。

8 个回答

11

你可以一起使用 statfnmatch 这两个模块来查找文件。

ST_MTIME 是指最后修改的时间。如果你想的话,可以选择其他的值。

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

然后你可以根据时间对文件列表进行排序,并根据这个顺序删除文件。

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

在Python中,你可以使用 os.walk(path) 这个功能来递归地遍历文件,也就是从一个文件夹开始,逐层查看里面的所有文件和子文件夹。而要获取文件的大小和最后修改时间,你可以用 os.stat(filename) 这个功能中的 st_sizest_mtime 属性。

28

嗯,Nadia的回答更接近你想问的内容。不过,如果你想在一个文件夹树中找到(唯一的)最旧文件,可以试试这个:

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)

稍微修改一下,你就可以找到 n 个最旧的文件(和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)

注意,使用 .endswith 方法可以让你这样调用:

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

这样可以选择多个文件扩展名。

最后,如果你想要一个完整的文件列表,按修改时间排序,以便删除尽可能多的文件来释放空间,这里有一些代码:

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)

注意,reverse=True 会把最旧的文件放在列表的最后,所以下一个要删除的文件,你只需要用 file_list.pop() 就可以了。

顺便说一下,为了彻底解决你的问题,因为你在使用Linux,那里可以用到 os.statvfs,你可以这样做:

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 是设备的空闲块数,而 statvfs.f_bsize 是块的大小。我们获取的是 rootfolder 的 statvfs,所以要注意任何指向其他设备的符号链接,这样我们可能会删除很多文件,但实际上并没有释放这个设备的空间。

更新(复制Juan的评论):

根据操作系统和文件系统的实现,你可能想把 f_bfree 乘以 f_frsize,而不是 f_bsize。在某些实现中,后者是首选的I/O请求大小。例如,在我刚测试的FreeBSD 9系统中,f_frsize是4096,而f_bsize是16384。POSIX说块计数字段是“以 f_frsize 为单位”(见 http://pubs.opengroup.org/onlinepubs/9699919799//basedefs/sys_statvfs.h.html

撰写回答