os.walk, не копаясь в каталогах ниже

Как мне ограничить os.walk возвратом файлов только из каталога, который я предоставил?

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        for f in files:
            if os.path.splitext(f)[1] in whitelist:
                outputList.append(os.path.join(root, f))
            else:
                self._email_to_("ignore")
    return outputList

person Setori    schedule 23.10.2008    source источник
comment
Другой случай, когда множество возможных подходов и все связанные с ними предостережения предполагают, что эту функциональность следует добавить в стандартную библиотеку Python.   -  person antred    schedule 31.10.2016
comment
files_with_full_path = [f.path for f in os.scandir(dir) if f.is_file()]. Если вам нужны только имена файлов, используйте f.name вместо f.path. Это самое быстрое решение и намного быстрее, чем любые walk или listdir, см. stackoverflow.com/a/40347279/2441026.   -  person user136036    schedule 24.01.2020


Ответы (20)


Используйте функцию walklevel.

import os

def walklevel(some_dir, level=1):
    some_dir = some_dir.rstrip(os.path.sep)
    assert os.path.isdir(some_dir)
    num_sep = some_dir.count(os.path.sep)
    for root, dirs, files in os.walk(some_dir):
        yield root, dirs, files
        num_sep_this = root.count(os.path.sep)
        if num_sep + level <= num_sep_this:
            del dirs[:]

Он работает так же, как os.walk, но вы можете передать ему параметр level, который указывает, насколько глубока рекурсия.

person nosklo    schedule 24.10.2008
comment
Действительно ли эта функция проходит через всю структуру, а затем удаляет записи ниже определенной точки? Или творится что-то более умное? Я даже не знаю, как это проверить с помощью кода. --python новичок - person mathtick; 19.08.2010
comment
@mathtick: когда какой-либо каталог находится на желаемом уровне или ниже, все его подкаталоги удаляются из списка подкаталогов для следующего поиска. Так что они не пройдут. - person nosklo; 19.08.2010
comment
Я просто поставил +1, потому что не мог удалить каталоги. Я пробовал dirs = [] и dirs = None, но они не работали. map(dirs.remove, dirs) работал, но с некоторыми напечатанными нежелательными сообщениями «[None]». Итак, почему именно del dirs[:]? - person Zach Young; 12.10.2012
comment
отличный ответ. +1 был бы только потому, что он работает с любым кодом, использующим os.walk. - person idanshmu; 10.12.2014
comment
Отличная функция - действительно полезная - person Doron Shai; 21.10.2015
comment
Обратите внимание, что это не работает при использовании topdown=False в os.walk. См. Четвертый абзац в документации: Modifying dirnames when topdown is False has no effect on the behavior of the walk, because in bottom-up mode the directories in dirnames are generated before dirpath itself is generated. - person dthor; 25.02.2016
comment
Я люблю это - person codyc4321; 21.08.2017
comment
@ZacharyYoung dirs = [] и dirs = None не будут работать, потому что они просто создают новый несвязанный объект и присваивают имя dirs. Исходный объект списка необходимо изменить на месте, а не имя dirs. - person nosklo; 01.10.2018
comment
Как я могу распечатать каталоги уровня 1 в этом скрипте python с учетом допустимой папки? Подождите, мне нужно научиться использовать yield. - person Timo; 24.12.2020
comment
Это отличный (действительно!) Ответ на другой вопрос. Поэтому количество кода намного больше, чем требуется. Другие ответы в этом потоке делают то же самое для конкретного запроса с гораздо меньшим количеством кода. - person mgueydan; 30.04.2021

Не используйте os.walk.

Пример:

import os

root = "C:\\"
for item in os.listdir(root):
    if os.path.isfile(os.path.join(root, item)):
        print item
person Yuval Adam    schedule 23.10.2008
comment
@ 576i: это не делает различий между файлами и каталогами - person ; 03.06.2016
comment
@Alexandr os.path.isfile и os.path.isdir позволяют различать. Я не понимаю, так как os.path.isfile находится в образце кода с '08, а ваш комментарий от '16. Это явно лучший ответ, поскольку вы не собираетесь просматривать каталог, а хотите его перечислить. - person Daniel F; 29.08.2017
comment
@DanielF, я имел в виду, что вам нужно перебрать все элементы, в то время как walk сразу дает вам отдельные списки каталогов и файлов. - person ; 29.08.2017
comment
Ах хорошо. На самом деле ответ Алекса кажется лучше (с использованием .next()), и он намного ближе к вашей идее. - person Daniel F; 29.08.2017
comment
Python 3.5 имеет функцию os.scandir, которая обеспечивает более сложное взаимодействие файла или каталога-объекта. См. мой ответ ниже - person ascripter; 27.05.2019

Я думаю, что решение на самом деле очень простое.

использовать

break

чтобы выполнить только первую итерацию цикла for, должен быть более элегантный способ.

for root, dirs, files in os.walk(dir_name):
    for f in files:
        ...
        ...
    break
...

В первый раз, когда вы вызываете os.walk, он возвращает тюльпаны для текущего каталога, а затем в следующем цикле содержимое следующего каталога.

Возьмите исходный сценарий и просто добавьте перерыв.

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        for f in files:
            if os.path.splitext(f)[1] in whitelist:
                outputList.append(os.path.join(root, f))
            else:
                self._email_to_("ignore")
        break
    return outputList
person Pieter    schedule 01.01.2014
comment
Это должен был быть принятый ответ. Простое добавление перерыва после цикла for f in files останавливает рекурсивность. Вы также можете убедиться, что topdown = True. - person Alecz; 31.10.2016
comment
Я просто хочу добавить этот комментарий и поблагодарить вас за то, что вы сэкономили мне время на работе за такой хороший упрощенный ответ. - person Steven Marsh; 15.07.2021

Предложение использовать listdir - хорошее. Прямой ответ на ваш вопрос в Python 2 - root, dirs, files = os.walk(dir_name).next().

Эквивалентный синтаксис Python 3 root, dirs, files = next(os.walk(dir_name))

person Alex Coventry    schedule 23.10.2008
comment
О, я получал от этого какую-то забавную ошибку. ValueError: слишком много значений для распаковки - person Setori; 24.10.2008
comment
Хороший! Хотя по ощущениям хакер. Например, когда вы включаете двигатель, но даете ему сделать только один оборот, а затем вытаскиваете ключ, чтобы он умер. - person Daniel F; 29.08.2017
comment
Наткнулся на это; root, dirs, files = os.walk(dir_name).next() дает мне AttributeError: 'generator' object has no attribute 'next' - person Evan; 27.11.2018
comment
@Evan, вероятно, потому, что это из 2008 года и использует синтаксис Python 2. В Python 3 вы можете написать root, dirs, files = next(os.walk(dir_name)), и тогда переменные root, dirs, files будут соответствовать только переменным генератора на уровне dir_name. - person CervEd; 01.03.2019

Вы можете использовать os.listdir(), который возвращает список имена (как для файлов, так и для каталогов) в данном каталоге. Если вам нужно различать файлы и каталоги, вызывайте os.stat() для каждого имени.

person Greg Hewgill    schedule 23.10.2008

Если у вас есть более сложные требования, чем только верхний каталог (например, игнорировать каталоги VCS и т. Д.), Вы также можете изменить список каталогов, чтобы предотвратить повторное прохождение через них os.walk.

ie:

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        dirs[:] = [d for d in dirs if is_good(d)]
        for f in files:
            do_stuff()

Примечание - будьте осторожны, чтобы изменить список, а не просто перепривязать его. Очевидно, что os.walk не знает о внешнем перепривязке.

person Brian    schedule 23.10.2008

Та же идея с listdir, но короче:

[f for f in os.listdir(root_dir) if os.path.isfile(os.path.join(root_dir, f))]
person Oleg Gryb    schedule 25.06.2014

Я чувствовал себя так, как будто бросил свои 2 пенса.

baselevel = len(rootdir.split("\\"))
for subdirs, dirs, files in os.walk(rootdir):
    curlevel = len(subdirs.split("\\"))
    if curlevel <= baselevel + 1:
        [do stuff]
person Matt R    schedule 02.06.2017
comment
Полезно, кроме \\ предполагает ОС Виндозе. Используйте 1_ - person pauljohn32; 14.06.2021

Начиная с Python 3.5, вы можете использовать _1 _ вместо os.listdir. Вместо строк вы получаете взамен итератор объектов DirEntry . Из документов:

Использование scandir() вместо listdir() может значительно повысить производительность кода, которому также требуется информация о типе файла или атрибутах файла, поскольку объекты DirEntry предоставляют эту информацию, если операционная система предоставляет ее при сканировании каталога. Все DirEntry методы могут выполнять системный вызов, но is_dir() и is_file() обычно требуют системного вызова только для символьных ссылок; DirEntry.stat() всегда требует системного вызова в Unix, но требует только одного для символьных ссылок в Windows.

Вы можете получить доступ к имени объекта через DirEntry.name, что эквивалентно выводу os.listdir

person ascripter    schedule 27.05.2019
comment
Вы не только можете использовать, но и должны использовать scandir(), так как он на много быстрее, чем listdir(). См. Тесты здесь: stackoverflow.com/a/40347279/2441026. - person user136036; 24.01.2020

В Python 3 мне удалось это сделать:

import os
dir = "/path/to/files/"

#List all files immediately under this folder:
print ( next( os.walk(dir) )[2] )

#List all folders immediately under this folder:
print ( next( os.walk(dir) )[1] )
person Jay Sheth    schedule 01.04.2016
comment
Это также работает для Python 2. Как получить второй уровень? - person ; 03.06.2016

Вы также можете сделать следующее:

for path, subdirs, files in os.walk(dir_name):
    for name in files:
        if path == ".": #this will filter the files in the current directory
             #code here
person Diana G    schedule 18.10.2012
comment
Не будет ли эта процедура без надобности перебирать все подкаталоги и файлы? - person Pieter; 27.02.2016

Вот как я это решил

if recursive:
    items = os.walk(target_directory)
else:
    items = [next(os.walk(target_directory))]

...
person Deifyed    schedule 06.01.2015

При использовании listdir есть загвоздка. Os.path.isdir (идентификатор) должен быть абсолютным путем. Чтобы выбрать подкаталоги, выполните следующие действия:

for dirname in os.listdir(rootdir):
  if os.path.isdir(os.path.join(rootdir, dirname)):
     print("I got a subdirectory: %s" % dirname)

Альтернативой является переход в каталог для проведения тестирования без использования os.path.join ().

person Kemin Zhou    schedule 23.09.2015

Вы можете использовать этот фрагмент

for root, dirs, files in os.walk(directory):
    if level > 0:
        # do some stuff
    else:
        break
    level-=1
person RousseauAlexandre    schedule 24.08.2016

создать список исключений, использовать fnmatch, чтобы пропустить структуру каталогов и выполнить процесс

excludes= ['a\*\b', 'c\d\e']
for root, directories, files in os.walk('Start_Folder'):
    if not any(fnmatch.fnmatch(nf_root, pattern) for pattern in excludes):
        for root, directories, files in os.walk(nf_root):
            ....
            do the process
            ....

то же, что и для "включает":

if **any**(fnmatch.fnmatch(nf_root, pattern) for pattern in **includes**):
person Hamsavardhini    schedule 21.11.2017

Почему бы просто не использовать range и os.walk в сочетании с zip? Не лучшее решение, но тоже подойдет.

Например так:

# your part before
for count, (root, dirs, files) in zip(range(0, 1), os.walk(dir_name)):
    # logic stuff
# your later part

У меня работает на python 3.

Также: break тоже попроще. (Посмотрите ответ от @Pieter)

person PiMathCLanguage    schedule 29.11.2018

Небольшое изменение в ответе Алекса, но с использованием __next__():

print(next(os.walk('d:/'))[2]) or print(os.walk('d:/').__next__()[2])

с [2], являющимся file в root, dirs, file, упомянутым в других ответах

person Oleg    schedule 30.01.2019

корневая папка изменяется для каждого каталога, который находит os.walk. Я решил эту проверку, если root == directory

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        if root == dir_name: #This only meet parent folder
            for f in files:
                if os.path.splitext(f)[1] in whitelist:
                    outputList.append(os.path.join(root, f))
                else:
                    self._email_to_("ignore")
    return outputList
person Pedro J. Sola    schedule 05.06.2019

Это хороший пример Python

def walk_with_depth(root_path, depth):
        if depth < 0:
            for root, dirs, files in os.walk(root_path):
                yield [root, dirs[:], files]

            return

        elif depth == 0:
            return

        base_depth = root_path.rstrip(os.path.sep).count(os.path.sep)
        for root, dirs, files in os.walk(root_path):
            yield [root, dirs[:], files]

            cur_depth = root.count(os.path.sep)
            
            if base_depth + depth <= cur_depth:
                del dirs[:]
person Alon Barad    schedule 23.12.2020

person    schedule
comment
Привет, Рич, добро пожаловать в Stack Overflow! Спасибо за этот фрагмент кода, который может предоставить некоторую краткосрочную помощь. Правильное объяснение значительно улучшило бы его долгосрочную ценность, показав, почему это хорошее решение проблемы, которое сделает его более полезным для будущих читателей, задав другие похожие вопросы. Пожалуйста, отредактируйте свой ответ, чтобы добавить пояснения, включая сделанные вами предположения. - person kenny_k; 30.09.2019