os.walk sans fouiller dans les répertoires ci-dessous


103

Comment limiter le os.walkretour des fichiers dans le répertoire que je fournis?

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
Un autre cas où la multitude d'approches possibles et toutes les mises en garde qui les accompagnent suggère que cette fonctionnalité devrait être ajoutée à la bibliothèque standard Python.
antred

files_with_full_path = [f.path for f in os.scandir(dir) if f.is_file()]. Si vous n'avez besoin que des noms de fichiers, utilisez à la f.nameplace de f.path. C'est la solution la plus rapide et beaucoup plus rapide que n'importe quelle autre walkou listdir, consultez stackoverflow.com/a/40347279/2441026 .
user136036 le

Réponses:


105

Utilisez la walklevelfonction.

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

Cela fonctionne exactement comme os.walk, mais vous pouvez lui passer un levelparamètre qui indique la profondeur de la récursivité.


3
Cette fonction «parcourt-elle» réellement la structure entière et supprime-t-elle les entrées en dessous d'un certain point? Ou est-ce qu'il se passe quelque chose de plus intelligent? Je ne sais même pas comment vérifier cela avec du code. --python débutant
mathtick

1
@mathtick: lorsqu'un répertoire sur ou en dessous du niveau souhaité est trouvé, tous ses sous-répertoires sont supprimés de la liste des sous-répertoires à rechercher ensuite. Donc, ils ne seront pas «marché».
nosklo

2
J'ai juste attribué +1 parce que j'avais du mal à "supprimer" les répertoires. J'avais essayé dirs = []et dirs = Nonemais cela n'a pas fonctionné. map(dirs.remove, dirs)a fonctionné, mais avec quelques messages indésirables «[Aucun]» imprimés. Alors, pourquoi del dirs[:]précisément?
Zach Young

4
Notez que cela ne fonctionne pas lors de l'utilisation topdown=Falsedans os.walk. Voir le 4e paragraphe dans la documentation :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 = []et dirs = Nonene fonctionnera pas car ils créent simplement un nouvel objet indépendant et attribuent le nom dirs. L'objet de liste d'origine doit être modifié sur place, pas le nom dirs.
nosklo

207

N'utilisez pas os.walk.

Exemple:

import os

root = "C:\\"
for item in os.listdir(root):
    if os.path.isfile(os.path.join(root, item)):
        print item

1
@ 576i: cela ne différencie pas les fichiers et les répertoires

4
@Alexandr os.path.isfileet os.path.isdirvous permet de vous différencier. Je ne comprends pas, car il os.path.isfileest dans l'exemple de code depuis '08 et votre commentaire est de '16. C'est clairement la meilleure réponse, car vous n'avez pas l'intention de parcourir un répertoire, mais de le répertorier.
Daniel F

@DanielF, ce que je voulais dire ici, c'est que vous devez boucler sur tous les éléments, tout en walkvous donnant immédiatement les listes séparées de répertoires et de fichiers.

Ah ok. En fait, la réponse d'Alex semble être meilleure (utilisation .next()) et elle est beaucoup plus proche de votre idée.
Daniel F

Python 3.5 a une os.scandirfonction qui permet une interaction fichier-ou-répertoire-objet plus sophistiquée. Voir ma réponse ci
ascripter

48

Je pense que la solution est en fait très simple.

utilisation

break

pour ne faire que la première itération de la boucle for, il doit y avoir une manière plus élégante.

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

La première fois que vous appelez os.walk, il renvoie des tulipes pour le répertoire courant, puis lors de la boucle suivante, le contenu du répertoire suivant.

Prenez le script original et ajoutez simplement une pause .

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
Cela aurait dû être la réponse acceptée. Le simple fait d'ajouter une "pause" après la boucle "for f in files" arrête la récursivité. Vous pouvez également vous assurer que topdown = True.
Alecz

23

La suggestion à utiliser listdirest bonne. La réponse directe à votre question dans Python 2 estroot, dirs, files = os.walk(dir_name).next() .

La syntaxe Python 3 équivalente est root, dirs, files = next(os.walk(dir_name))


1
Oh, je recevais toutes sortes d'erreurs amusantes de celui-là. ValueError: trop de valeurs à décompresser
Setori

1
Agréable! Cela ressemble à un hack, cependant. Comme lorsque vous allumez un moteur mais que vous ne le laissez faire qu'un tour, puis que vous tirez sur la clé pour le laisser mourir.
Daniel F

Je suis tombé sur ceci; root, dirs, files = os.walk(dir_name).next()me donneAttributeError: 'generator' object has no attribute 'next'
Evan

3
@Evan, probablement parce que cela date de 2008 et utilise la syntaxe Python 2. En Python 3, vous pouvez écrire root, dirs, files = next(os.walk(dir_name))et ensuite les variables root, dirs, filesne correspondront qu'aux variables du générateur au dir_nameniveau.
CervEd

13

Vous pouvez utiliser os.listdir()qui renvoie une liste de noms (pour les fichiers et les répertoires) dans un répertoire donné. Si vous avez besoin de faire la distinction entre les fichiers et les répertoires, appelez os.stat()chaque nom.


9

Si vous avez des exigences plus complexes que le seul répertoire supérieur (par exemple, ignorer les répertoires VCS, etc.), vous pouvez également modifier la liste des répertoires pour empêcher os.walk de les parcourir.

c'est à dire:

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()

Remarque - veillez à modifier la liste, plutôt que de simplement la relier. De toute évidence, os.walk ne connaît pas la reliure externe.


6
for path, dirs, files in os.walk('.'):
    print path, dirs, files
    del dirs[:] # go only one level deep

4

La même idée avec listdir , mais en plus court:

[f for f in os.listdir(root_dir) if os.path.isfile(os.path.join(root_dir, f))]

3

J'avais l'impression de jeter mes 2 pence.

baselevel = len(rootdir.split("\\"))
for subdirs, dirs, files in os.walk(rootdir):
    curlevel = len(subdirs.split("\\"))
    if curlevel <= baselevel + 1:
        [do stuff]

2

En Python 3, j'ai pu faire ceci:

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] )

Cela fonctionne également pour Python 2. Comment obtenir le deuxième niveau?

2

Depuis Python 3.5, vous pouvez utiliser à la os.scandirplace de os.listdir. Au lieu de chaînes, vous obtenez un itérateur d' DirEntryobjets en retour. À partir de la documentation:

L'utilisation scandir()au lieu de listdir()peut augmenter considérablement les performances du code qui a également besoin d'informations sur le type de fichier ou les attributs de fichier, car les DirEntryobjets exposent ces informations si le système d'exploitation les fournit lors de l'analyse d'un répertoire. Toutes les DirEntryméthodes peuvent effectuer un appel système, mais is_dir()et is_file()nécessitent généralement un appel système pour les liens symboliques; DirEntry.stat()nécessite toujours un appel système sous Unix mais n'en nécessite qu'un pour les liens symboliques sous Windows.

Vous pouvez accéder au nom de l'objet via DirEntry.namequi équivaut alors à la sortie deos.listdir


1
Non seulement « peut » que vous utilisez, vous devez utiliser scandir(), car il est un beaucoup plus rapide que listdir(). Consultez les benchmarks ici: stackoverflow.com/a/40347279/2441026 .
user136036 le

1

Vous pouvez également effectuer les opérations suivantes:

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
Cette boucle à travers tous les sous-répertoires et fichiers ne sera-t-elle pas inutilement?
Pieter

0

Voilà comment je l'ai résolu

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

...

0

Il y a un problème lors de l'utilisation de listdir. Le os.path.isdir (identificateur) doit être un chemin absolu. Pour choisir les sous-répertoires que vous faites:

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

L'alternative est de passer au répertoire pour faire les tests sans os.path.join ().


0

Vous pouvez utiliser cet extrait

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

0

créer une liste d'exclusions, utiliser fnmatch pour ignorer la structure du répertoire et effectuer le processus

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
            ....

idem pour «comprend»:

if **any**(fnmatch.fnmatch(nf_root, pattern) for pattern in **includes**):

0

Pourquoi ne pas simplement utiliser un rangeet os.walkcombiné avec le zip? Ce n'est pas la meilleure solution, mais cela fonctionnerait aussi.

Par exemple comme ceci:

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

Fonctionne pour moi sur python 3.

Aussi: A breakest plus simple trop btw. (Regardez la réponse de @Pieter)


0

Un léger changement dans la réponse d'Alex, mais en utilisant __next__() :

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

avec l' [2]étant fileen root, dirs, filementionné dans d' autres réponses


0

le dossier racine change pour chaque répertoire trouvé par os.walk. Je résolve cela en vérifiant si le répertoire racine ==

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
Salut Rich, bienvenue dans Stack Overflow! Merci pour cet extrait de code, qui pourrait fournir une aide limitée à court terme. Une explication appropriée améliorerait considérablement sa valeur à long terme en montrant pourquoi c'est une bonne solution au problème, et la rendrait plus utile aux futurs lecteurs avec d'autres questions similaires. Veuillez modifier votre réponse pour ajouter des explications, y compris les hypothèses que vous avez formulées.
kenny_k
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.