Найти самый старый файл (рекурсивно) в каталоге - PullRequest
10 голосов
/ 08 мая 2009

Я пишу скрипт резервного копирования Python, и мне нужно найти самый старый файл в каталоге (и его подкаталогах). Мне также нужно отфильтровать его только до * .avi файлов.

Сценарий всегда будет работать на компьютере с Linux. Есть ли какой-нибудь способ сделать это в Python или было бы лучше выполнить некоторые команды оболочки?

В данный момент я запускаю df, чтобы получить свободное место на определенном разделе, и если осталось менее 5 гигабайт, я хочу начать удаление самых старых файлов *.avi, пока не будет выполнено это условие.

Ответы [ 7 ]

22 голосов
/ 08 мая 2009

Hm. Ответ Нади ближе к тому, что вы хотели спросить; однако для поиска (одного) самого старого файла в дереве попробуйте следующее:

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 самые старые файлы (аналогично ответу Нади):

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, так что учтите любые символические ссылки, указывающие на другие устройства, где мы могли бы удалить много файлов, фактически не освобождая место на этом устройстве.

ОБНОВЛЕНИЕ (копирование комментария Хуана):

В зависимости от ОС и реализации файловой системы, вы можете умножить f_bfree на f_frsize, а не на f_bsize. В некоторых реализациях последний является предпочтительным размером запроса ввода-вывода. Например, в системе FreeBSD 9, которую я только что протестировал, f_frsize был 4096, а f_bsize был 16384. POSIX говорит, что поля счетчиков блоков «в единицах f_frsize» (см. http://pubs.opengroup.org/onlinepubs/9699919799//basedefs/sys_statvfs.h.html)

13 голосов
/ 08 мая 2009

Чтобы сделать это в Python, вы можете использовать os.walk(path) для рекурсивного перебора файлов, а атрибуты st_size и st_mtime os.stat(filename) для размеры файлов и время модификации.

10 голосов
/ 08 мая 2009

Вы можете использовать stat и fnmatch вместе, чтобы найти файлы

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])
7 голосов
/ 08 мая 2009

Я думаю, что самый простой способ сделать это - использовать find вместе с ls -t (сортировать файлы по времени).

что-то в этом духе должно сработать (удаляет самый старый файл avi в указанной директории)

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

шаг за шагом ....

find / -name "* .avi" - найти все avi-файлы рекурсивно, начиная с корневого каталога

xargs ls -t - отсортировать все файлы, найденные по времени модификации, от самого нового до самого старого.

tail -n 1 - получить последний файл в списке (самый старый)

xargs rm - и удалите его

3 голосов
/ 08 мая 2009

Вот еще одна формулировка Python, которая немного устаревшая по сравнению с некоторыми другими, но ее легко изменить, и она обрабатывает случай отсутствия подходящих файлов без выдачи исключения.

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()
2 голосов
/ 08 мая 2009

Проверьте команду linux find.

В качестве альтернативы, этот пост объединяет ls и tail для удаления самого старого файла в каталоге. Это можно сделать в цикле, когда свободного места недостаточно.

Для справки, вот код оболочки, который это делает (перейдите по ссылке для получения дополнительной альтернативы и обсуждения):

ls -t -r -1 /path/to/files | head --lines 1 | xargs rm
0 голосов
/ 08 мая 2009

Модуль os предоставляет функции, необходимые для получения списков каталогов и информации о файлах в Python. Я обнаружил, что os.walk особенно полезен для рекурсивного хождения по каталогам, и os.stat предоставит вам подробную информацию (включая время изменения) для каждой записи.

Вы можете сделать это проще с помощью простой команды оболочки. Будет ли это работать лучше для вас или нет, зависит от того, что вы хотите сделать с результатами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...