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

105

Как мне ограничить 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
Сетори
источник
2
Другой случай, когда множество возможных подходов и все связанные с ними оговорки предполагают, что эту функциональность следует добавить в стандартную библиотеку Python.
Antred
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 .
user136036

Ответы:

105

Используйте 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параметр, который указывает, насколько глубока рекурсия.

носкло
источник
3
Действительно ли эта функция «проходит» по всей структуре, а затем удаляет записи ниже определенной точки? Или творится что-то более умное? Я даже не знаю, как это проверить с помощью кода. --python beginner
mathtick
1
@mathtick: когда какой-либо каталог находится на желаемом уровне или ниже, все его подкаталоги удаляются из списка подкаталогов для следующего поиска. Так что они не будут "гулять".
nosklo
2
Я просто поставил +1, потому что не мог "удалить" каталоги. Я попробовал dirs = []и , dirs = Noneно те не работают. map(dirs.remove, dirs)работал, но с некоторыми напечатанными нежелательными сообщениями «[Нет]». Так почему del dirs[:]именно?
Зак Янг
4
Обратите внимание, что это не работает при использовании topdown=Falseв os.walk. См. 4-й абзац в документации :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.
dthor
3
@ZacharyYoung dirs = []и dirs = Noneне будет работать, потому что они просто создают новый несвязанный объект и присваивают имя dirs. Исходный объект списка необходимо изменить на месте, а не в имени dirs.
носкло 01
207

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

Пример:

import os

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

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

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

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
Питер
источник
9
Это должен был быть принятый ответ. Простое добавление «break» после цикла «for f in files» останавливает рекурсивность. Вы также можете убедиться, что topdown = True.
Alecz
23

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

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

Алекс Ковентри
источник
1
О, я получал от этого какую-то забавную ошибку. ValueError: слишком много значений для распаковки
Сетори,
1
Ницца! Хотя по ощущениям хакер. Например, когда вы включаете двигатель, но даете ему сделать только один оборот, а затем вытаскиваете ключ, чтобы он умер.
Daniel F
Наткнулся на это; root, dirs, files = os.walk(dir_name).next()дает мнеAttributeError: 'generator' object has no attribute 'next'
Эван
3
@Evan, вероятно, потому, что это с 2008 года и использует синтаксис Python 2. В Python 3 вы можете писать, root, dirs, files = next(os.walk(dir_name))и тогда переменные root, dirs, filesбудут соответствовать только переменным генератора на dir_nameуровне.
CervEd 01
13

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

Грег Хьюгилл
источник
9

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

то есть:

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 не знает о внешнем перепривязке.

Брайан
источник
6
for path, dirs, files in os.walk('.'):
    print path, dirs, files
    del dirs[:] # go only one level deep
мастерxilo
источник
4

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

[f for f in os.listdir(root_dir) if os.path.isfile(os.path.join(root_dir, f))]
Олег Гриб
источник
3

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

baselevel = len(rootdir.split("\\"))
for subdirs, dirs, files in os.walk(rootdir):
    curlevel = len(subdirs.split("\\"))
    if curlevel <= baselevel + 1:
        [do stuff]
Мэтт Р.
источник
2

В 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] )
Джей Шет
источник
Это также работает для Python 2. Как получить второй уровень?
2

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

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

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

аскриптер
источник
1
Мало того, что вы можете использовать, вы должны использовать scandir(), так как это намного быстрее, чем listdir(). См. Тесты здесь: stackoverflow.com/a/40347279/2441026 .
user136036
1

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

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
Диана Г
источник
2
Не будет ли эта процедура без необходимости перебирать все подкаталоги и файлы?
Питер
0

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

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

...
Обожествленный
источник
0

При использовании 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 ().

Кемин Чжоу
источник
0

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

for root, dirs, files in os.walk(directory):
    if level > 0:
        # do some stuff
    else:
        break
    level-=1
РуссоАлександр
источник
0

создать список исключений, использовать 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**):
Хамсавардхини
источник
0

Почему бы просто не использовать 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.

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

PiMathCLanguage
источник
0

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

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

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

Олег
источник
0

корневая папка изменяется для каждого каталога, который находит 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
Педро Х. Сола
источник
0
import os

def listFiles(self, dir_name):
    names = []
    for root, directory, files in os.walk(dir_name):
        if root == dir_name:
            for name in files:
                names.append(name)
    return names
Богатый
источник
1
Привет, Рич, добро пожаловать в Stack Overflow! Спасибо за этот фрагмент кода, который может предоставить некоторую краткосрочную помощь. Правильное объяснение значительно повысило бы его ценность в долгосрочной перспективе, показав, почему это хорошее решение проблемы, и сделало бы его более полезным для будущих читателей с другими подобными вопросами. Пожалуйста , измените свой ответ , чтобы добавить некоторые объяснения, в том числе допущений , которые вы сделали.
kenny_k