Есть ли способ эффективно вывести каждый файл в каталог, содержащий миллионы файлов? - PullRequest
12 голосов
/ 23 февраля 2011

Мне известно о 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), то поддержание позиционного состояния не будет затронуто, но некоторые файлы могут быть пропущены, так как их индексы могут быть перемещены в индекс «позади»iterator.

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

Редактировать:

Операционная система Redhat.Мой пример использования такой:

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

Редактировать:

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

Прилагательное 1. Хорошо обосновано или оправдано, уместно.

(Извините, С.Лотт, я не смог устоять).

Я редактировал абзац ввопрос выше.

Ответы [ 6 ]

13 голосов
/ 23 февраля 2011

tl; dr : Начиная с Python 3.5 (в настоящее время в бета-версии) просто используйте os.scandir

Как я уже писал ранее, поскольку "iglob" - это просто фасад для реального итератора, вам придется вызывать системные функции низкого уровня, чтобы получать по одной за раз, как вы хотите. Скорее всего, это выполнимо из Python. Если вы не сказали, находитесь ли вы в системе Posix (Linux / Mac OS X / 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

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

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

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

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

9 голосов
/ 23 февраля 2011

Глобальный модуль 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
8 голосов
/ 23 февраля 2011

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

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

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

6 голосов
/ 12 августа 2011
Пост

@ 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 голосов
/ 23 февраля 2011

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

Ни один метод не покажет имя файла, которое "изменилось". Непонятно даже, что вы имеете в виду под «изменением имен файлов, добавлением новых файлов и удалением файлов»? Какой у вас вариант использования?

Допустим, у вас есть три файла: 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 записывает файлы и ставит в очередь добавить / изменить / удалить памятку в очередь.

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

1 голос
/ 23 февраля 2011

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

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

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

...