Подтвердить что ты не робот

Есть ли способ эффективно получить каждый файл в каталоге, содержащем миллионы файлов?

Я знаю os.listdir, но, насколько я могу судить, он получает все имена файлов в каталоге в память и затем возвращает список. То, что я хочу, - это способ дать имя файла, работать над ним, а затем дать следующий, не читая их все в памяти.

Есть ли способ сделать это? Я беспокоюсь о том, где имена файлов меняются, новые файлы добавляются, и файлы удаляются с использованием такого метода. Некоторые итераторы не позволяют вам изменять коллекцию во время итерации, в основном, делая снимок состояния коллекции в начале и сравнивая это состояние с каждой операцией move. Если есть итератор, способный давать имена файлов из пути, вызывает ли он ошибку, если есть изменения в файловой системе (добавление, удаление, переименование файлов в итерированном каталоге), которые изменяют коллекцию?

Возможно, может быть несколько случаев, которые могут привести к сбою итератора, и все зависит от того, как итератор поддерживает состояние. Использование примера S.Lotts:

filea.txt
fileb.txt
filec.txt

Итератор дает filea.txt. Во время processing, filea.txt переименовывается в filey.txt, а fileb.txt переименовывается в filez.txt. Когда итератор пытается получить следующий файл, если он должен использовать имя файла filea.txt, чтобы найти его текущую позицию, чтобы найти следующий файл, а filea.txt - нет, что произойдет? Возможно, он не сможет восстановить эту позицию в коллекции. Точно так же, если итератор должен был получить fileb.txt при получении filea.txt, он мог бы искать положение fileb.txt, терпеть неудачу и вызывать ошибку.

Если вместо этого итератор смог каким-то образом поддерживать индекс dir.get_file(0), то сохранение состояния позиционирования не будет затронуто, но некоторые файлы могут быть пропущены, так как их индексы могут быть перенесены в индекс "позади" итератора.

Это все теоретически, конечно, поскольку, похоже, нет встроенного (python) способа повторения файлов в каталоге. Однако есть несколько отличных ответов, которые решают проблему, используя очереди и уведомления.

Edit:

ОС беспокойства - Редхат. Моим вариантом использования является следующее:

Процесс A непрерывно записывает файлы в хранилище. Процесс B (тот, который я пишу), будет итерировать эти файлы, выполнять некоторую обработку на основе имени файла и перемещать файлы в другое место.

Edit:

Определение действительного:

Прилагательное 1. Хорошо обоснованные или обоснованные, соответствующие.

(Извините S.Lott, я не удержался).

Я редактировал данный параграф выше.

4b9b3361

Ответ 1

tl; dr <update> : По состоянию на Python 3.5 (в настоящее время в бета-версии) просто используйте os.scandir </обновление >

Как я уже писал ранее, поскольку "iglob" - это просто фасад реального итератора, вам придется вызывать системные функции низкого уровня, чтобы получить по одному, как вы хотите. Fortyuantelly, это выполнимо с Python. Если вы не сказали, что вы находитесь в системе Posix (Linux/Mac OS X/other Unix) или Windows. В последнем случае вы должны проверить, имеет ли win32api какой-либо вызов для чтения "следующей записи из каталога" или как действовать иначе.

В первом случае вы можете перейти к вызову функций libc прямо через ctypes и получить запись файла файла, включая информацию об именах).

Документация по функциям C приведена здесь: http://www.gnu.org/s/libc/manual/html_node/Opening-a-Directory.html#Opening-a-Directory

http://www.gnu.org/s/libc/manual/html_node/Reading_002fClosing-Directory.html#Reading_002fClosing-Directory

К сожалению, структура "dirent64" C определяется во время компиляции C для каждой системы - я понял, что в моей системе и в большинстве случаев это будет похоже на то, что я положил ее на Python на сниппет, но вы могли бы хочу checj ваш "dirent.h" и другие параметры, которые он включает в /usr/includes.

Вот фрагмент с использованием ctypes и libC, которые я собрал вместе, чтобы вы могли получить каждое имя файла и выполнить на нем действия. Обратите внимание, что ctypes automaticaly дает вам строку Python, когда вы выполняете str (...) в массиве char, определенном в структуре. (Я использую оператор print, который неявно вызывает Python str)

from ctypes import *
libc = cdll.LoadLibrary( "libc.so.6")
 dir_ = c_voidp( libc.opendir("/home/jsbueno"))

class Dirent(Structure):
    _fields_ = [("d_ino",  c_voidp),
                ("off_t", c_int64),
                ("d_reclen", c_ushort),
                ("d_type", c_ubyte),
                ("d_name", c_char * 2048)
            ]

while True:
    p  = libc.readdir64(dir_)
    if not p:
        break
    entry = Dirent.from_address( p)
    print entry.d_name

update: Python 3.5 теперь находится в бета-версии, и в этой версии новый вызов функции os.scandir доступен как материализация PEP 471 ( "лучший и быстрый итератор каталогов" ), который делает именно то, что предлагается здесь, помимо многих других оптимизаций, которые могут доставлять до 9-кратного увеличения скорости по сравнению с os.listdir в списке крупных каталогов в Windows (увеличение в системах Posix в 2-3 раза).

Ответ 2

Модуль glob Python с 2,5 года имеет метод iglob, который возвращает итератор. Итератор предназначен именно для того, чтобы не хранить огромные значения в памяти.

glob.iglob(pathname)
Return an iterator which yields the same values as glob() without
actually storing them all simultaneously.

Например:

import glob
for eachfile in glob.iglob('*'):
    # act upon eachfile

Ответ 3

Поскольку вы используете Linux, вы можете посмотреть pyinotify. Это позволит вам написать Python script, который контролирует каталог изменений файловой системы - например, создание, изменение или удаление файлов.

Каждый раз, когда происходит такое событие файловой системы, вы можете организовать для Python script вызов функции. Это примерно так же, как получение каждого имени файла один раз, а также возможность реагировать на изменения и удаления.

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

Ответ 4

Что я хочу, это способ дать имя файла, работать над ним, а затем дать следующий, не читая их все в памяти.

Ни один метод не покажет имя файла, который "изменился". Он даже не понимает, что вы подразумеваете под этим "изменением имен файлов, добавляются новые файлы и удаляются файлы"? Каков ваш прецедент?

Скажем, у вас есть три файла: a.a, b.b, c.c.

Ваш магический "итератор" начинается с a.a. Вы его обрабатываете.

Волшебный "итератор" переходит в b.b. Вы обрабатываете его.

Между тем a.a копируется в a1.a1, a.a удаляется. Что теперь? Что делает ваш магический итератор с этим? Он уже прошел a.a. Поскольку a1.a1 до b.b, он никогда не увидит его. Что должно произойти при "изменении имен файлов, добавлении новых файлов и удалении файлов"?

Волшебный "итератор" переходит в c.c. Что должно было случиться с другими файлами? И как вы должны были узнать об исключении?


Процесс A непрерывно записывает файлы в хранилище. Процесс B (тот, который я пишу), будет итерировать эти файлы, выполнять некоторую обработку на основе имени файла и перемещать файлы в другое место.

Не используйте голую файловую систему для координации.

Используйте очередь.

Процесс A записывает файлы и помещает добавление/изменение/удаление memmento в очередь.

Процесс B считывает память из очереди, а затем выполняет последующую обработку в файле, названном в память.

Ответ 5

Сообщение

@jsbueno действительно полезно, но по-прежнему выглядит медленным на медленных дисках, поскольку libc readdir() только готовый 32K записей на диске за раз. Я не специалист по системным вызовам непосредственно в python, но я изложил, как писать код на C, который будет перечислять каталог с миллионами файлов, в сообщении в блоге по адресу: http://www.olark.com/spw/2011/08/you-can-list-a-directory-with-8-million-files-but-not-with-ls/.

Идеальный случай заключался бы в вызове getdents() непосредственно в python ( http://www.kernel.org/doc/man-pages/online/pages/man2/getdents.2.html, поэтому вы можете указать размер буфера чтения при загрузке записей каталога с диска.

Вместо вызова readdir(), который, насколько я могу судить, имеет размер буфера, определенный во время компиляции.

Ответ 6

Я думаю, что то, что вы просите, невозможно из-за характера файла IO. Когда python получил список каталогов, он не может поддерживать представление о фактическом каталоге на диске, и нет никакого способа для python настаивать на том, чтобы ОС информировала его о любых изменениях в каталоге.

Все, что может сделать python, это запрашивать периодические списки и анализировать результаты, чтобы увидеть, были ли какие-либо изменения.

Самое лучшее, что вы можете сделать, это создать файл семафора в каталоге, который позволяет другим процессам узнать, что ваш процесс python желает, чтобы ни один другой процесс не изменял каталог. Конечно, они будут наблюдать только семафор, если вы явно запрограммировали их.