python os.walk до определенного уровня

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

import os

folders = ['Y:\\path1', 'Y:\\path2', 'Y:\\path3']
for stuff in folders:
    for root, dirs, files in os.walk(stuff, topdown=True):
        print("there are", len(files), "files in", root)

Это прекрасно работает, пока в "основной" папке не будет несколько папок, поскольку она может возвращать длинный, бесполезный список файлов из-за плохого управления папками / файлами. Так что я бы хотел перейти только на второй уровень. пример:

Main Folder
---file_i_want
---file_i_want
---Sub_Folder
------file_i_want <--*
------file_i want <--*
------Sub_Folder_2
---------file_i_dont_want
---------file_i_dont_want

Я знаю, как перейти на первый уровень с break и с del dirs[:] взято из этого поста, а также из этого поста.

import os
import pandas as pd

folders = ['Y:\\path1', 'Y:\\path2', 'Y:\\path3']
for stuff in folders:
    for root, dirs, files in os.walk(stuff, topdown=True):
        print("there are", len(files), "files in", root)
        del dirs[:] # or a break here. does the same thing.

Но независимо от моих поисков, я не могу понять, как пройти два уровня. Может я просто не разбираюсь в других постах по этому поводу или как? Я думал что-то вроде del dirs[:2] но безрезультатно. Может кто-нибудь направить меня или объяснить мне, как это сделать?

2 ответа

Решение

Вы могли бы сделать так:

for root,dirs,files in os.walk(stuff):
    if root[len(stuff)+1:].count(os.sep)<2:
        for f in files:
            print(os.path.join(root,f))

ключ это: if root[len(stuff)+1:].count(os.sep)<2

Удаляет stuff+ разделитель от root, поэтому результат относительно stuff, Просто посчитайте количество разделителей файлов, и не вводите условие, если вы не получите 0 или 1 разделители.

Конечно, он по-прежнему сканирует полную файловую структуру, но если это не очень глубоко, это сработает.

Другое решение было бы использовать только os.listdir рекурсивно (с проверкой каталогов) с максимальным уровнем рекурсии, но это немного сложнее, если вам это не нужно. Поскольку это не так сложно, вот одна из реализаций:

def scanrec(root):
    rval = []

    def do_scan(start_dir,output,depth=0):
        for f in os.listdir(start_dir):
            ff = os.path.join(start_dir,f)
            if os.path.isdir(ff):
                if depth<2:
                    do_scan(ff,output,depth+1)
            else:
                output.append(ff)

    do_scan(root,rval,0)
    return rval

print(scanrec(stuff))  # prints the list of files not below 2 deep

Замечания: os.listdir а также os.path.isfile выполнить 2 stat звонки так не оптимальны. В Python 3.5 использование os.scandir мог бы избежать этого двойного звонка.

Вы можете сосчитать разделители и, если он имеет два уровня, удалить содержимое dirs так walk глубже не повторяется:

import os

MAX_DEPTH = 2
folders = ['Y:\\path1', 'Y:\\path2', 'Y:\\path3']
for stuff in folders:
    for root, dirs, files in os.walk(stuff, topdown=True):
        print("there are", len(files), "files in", root)
        if root.count(os.sep) - stuff.count(os.sep) == MAX_DEPTH - 1:
            del dirs[:]

Документация Python гласит следующее о поведении:

Когда topdown имеет значение True, вызывающий может изменить список dirnames на месте (возможно, используя del или slice назначением), и walk() будет возвращаться только в подкаталоги, чьи имена остаются в dirnames; это может использоваться для сокращения поиска, наложения определенного порядка посещения или даже для информирования walk() о каталогах, которые вызывающий абонент создает или переименовывает, прежде чем он возобновит walk() снова.

Обратите внимание, что вам необходимо учитывать разделители, присутствующие в folders, Например, когда y:\path1 гулял корень y:\path но вы не хотите останавливать рекурсию там.

Другие вопросы по тегам