web-dev-qa-db-fra.com

python os.walk à un certain niveau

Je veux créer un programme qui utilise du code de base pour lire un dossier et me dire combien de fichiers se trouvent dans le dossier. Voici comment je fais cela actuellement:

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)

Cela fonctionne très bien jusqu'à ce qu'il y ait plusieurs dossiers à l'intérieur du dossier "principal" car il peut renvoyer une longue liste de fichiers indésirable en raison d'une mauvaise gestion des dossiers/fichiers. Je ne voudrais donc aller qu'au deuxième niveau tout au plus. exemple:

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

Je sais comment aller uniquement au premier niveau avec un break et avec del dirs[:] extrait de cet article et également cet article .

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.

Mais peu importe ma recherche, je ne peux pas savoir comment aller en profondeur. Je ne comprends peut-être pas les autres messages ou quelque chose? Je pensais à quelque chose comme del dirs[:2] mais en vain. Quelqu'un peut-il me guider ou m'expliquer comment y parvenir?

12
MattR

vous pourriez faire comme ça:

depth = 2

# [1] abspath() already acts as normpath() to remove trailing os.sep
#, and we need ensures trailing os.sep not exists to make slicing accurate. 
# [2] abspath() also make /../ and ////, "." get resolved even though os.walk can returns it literally.
# [3] expanduser() expands ~
# [4] expandvars() expands $HOME
stuff = os.path.abspath(os.path.expanduser(os.path.expandvars(stuff)))

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

la clé est: if root[len(stuff):].count(os.sep) < depth

Il supprime stuff de root, donc le résultat est relatif à stuff. Il suffit de compter le nombre de séparateurs de fichiers.

La profondeur agit comme la commande find trouvée sous Linux, c'est-à-dire que -maxdepth 0 Signifie ne rien faire, -maxdepth 1 Ne scanne que les fichiers de premier niveau et -maxdepth 2 Scanne les fichiers inclus sous -annuaire.

Bien sûr, il analyse toujours la structure complète du fichier, mais à moins qu'il ne soit très profond, cela fonctionnera.

Une autre solution serait d'utiliser uniquement os.listdir Récursivement (avec vérification du répertoire) avec un niveau de récursivité maximum, mais c'est un peu plus compliqué si vous n'en avez pas besoin. Comme ce n'est pas si difficile, voici une implémentation:

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

Remarque: os.listdir Et os.path.isfile Effectuent 2 stat appels donc pas optimal. Dans Python 3.5, l'utilisation de os.scandir Pourrait éviter ce double appel.

13

Vous pouvez compter les séparateurs et s'il s'agit de deux niveaux de profondeur, supprimez le contenu de dirs afin que walk ne récapitule pas plus profondément:

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[:]

documentation Python indique ce qui suit concernant le comportement:

Lorsque topdown a la valeur True, l'appelant peut modifier la liste des noms de répertoire sur place (peut-être en utilisant l'affectation del ou slice), et walk () ne récursera que dans les sous-répertoires dont les noms restent dans les noms de répertoire; cela peut être utilisé pour élaguer la recherche, imposer un ordre de visite spécifique ou même pour informer walk () des répertoires que l'appelant crée ou renomme avant de reprendre walk ().

Notez que vous devez prendre en compte les séparateurs présents dans le folders. Par exemple, lorsque y:\path1 est parcouru root est y:\path mais vous ne voulez pas arrêter la récursion ici.

4
niemmi