Wie kann ich mit dem glob.glob-Modul nach Unterordnern suchen?

106

Ich möchte eine Reihe von Unterordnern in einem Ordner öffnen, einige Textdateien suchen und einige Zeilen der Textdateien drucken. Ich benutze dies:

configfiles = glob.glob('C:/Users/sam/Desktop/file1/*.txt')

Dies kann jedoch auch nicht auf die Unterordner zugreifen. Weiß jemand, wie ich mit demselben Befehl auch auf Unterordner zugreifen kann?

UserYmY
quelle

Antworten:

163

Verwenden Sie in Python 3.5 und höher die neue rekursive **/Funktionalität:

configfiles = glob.glob('C:/Users/sam/Desktop/file1/**/*.txt', recursive=True)

Wenn recursivefestgelegt, **gefolgt von einem Pfadtrennzeichen, das mit 0 oder mehr Unterverzeichnissen übereinstimmt.

In früheren Python-Versionen glob.glob()können Dateien in Unterverzeichnissen nicht rekursiv aufgelistet werden.

In diesem Fall würde ich stattdessen os.walk()kombiniert mit fnmatch.filter()verwenden:

import os
import fnmatch

path = 'C:/Users/sam/Desktop/file1'

configfiles = [os.path.join(dirpath, f)
    for dirpath, dirnames, files in os.walk(path)
    for f in fnmatch.filter(files, '*.txt')]

Dadurch werden Ihre Verzeichnisse rekursiv durchsucht und alle absoluten Pfadnamen an übereinstimmende .txtDateien zurückgegeben. In diesem speziellen Fall fnmatch.filter()kann es zu einem Overkill kommen. Sie können auch einen .endswith()Test verwenden:

import os

path = 'C:/Users/sam/Desktop/file1'

configfiles = [os.path.join(dirpath, f)
    for dirpath, dirnames, files in os.walk(path)
    for f in files if f.endswith('.txt')]
Martijn Pieters
quelle
3
Ich kann sehen: glob.glob ('/ Pfad zum Verzeichnis / * / *. Txt ") funktioniert für mich. Dies verwendet im Wesentlichen die Unix-Shell-Regel.
Surya
7
@ User123: Verzeichnisse werden nicht rekursiv aufgelistet . Sie listen alle Textdateien eine Ebene tief auf , jedoch nicht in weiteren Unterverzeichnissen oder sogar direkt in path to directory.
Martijn Pieters
1
Dies hängt nicht vollständig zusammen, aber warum liefert die Einstellung recursive=Falsezusammen mit der **/ Funktionalität nicht die Liste der Dateien nur im angegebenen Ordner, sondern in den untergeordneten Ordnern?
Dr_Zaszuś
@ Dr_Zaszuś: Entschuldigung? **/Gibt eine Liste der Verzeichnisnamen im aktuellen Arbeitsverzeichnis an, da das Muster mit endet /und recursive=FalseSie im Grunde genommen ein Doppel haben *, das genauso übereinstimmt wie */, nur weniger effizient.
Martijn Pieters
@ Dr_Zaszuś: Verwenden */*Sie diese Option, wenn Sie alle Dateien in allen Unterverzeichnissen benötigen.
Martijn Pieters
22

So finden Sie Dateien in unmittelbaren Unterverzeichnissen:

configfiles = glob.glob(r'C:\Users\sam\Desktop\*\*.txt')

Für eine rekursive Version, die alle Unterverzeichnisse durchläuft, können Sie seit Python 3.5 Folgendes verwenden **und übergeben :recursive=True

configfiles = glob.glob(r'C:\Users\sam\Desktop\**\*.txt', recursive=True)

Beide Funktionsaufrufe geben Listen zurück. Sie können glob.iglob()Pfade einzeln zurückgeben. Oder verwenden Siepathlib :

from pathlib import Path

path = Path(r'C:\Users\sam\Desktop')
txt_files_only_subdirs = path.glob('*/*.txt')
txt_files_all_recursively = path.rglob('*.txt') # including the current dir

Beide Methoden geben Iteratoren zurück (Sie können Pfade einzeln abrufen).

jfs
quelle
Ja, das habe ich verstanden; Aber ich hatte auch nicht erwartet glob(), Muster in Verzeichnissen zu unterstützen.
Martijn Pieters
Kommentar gelöscht, ich sehe jetzt, dass es den falschen Eindruck gab; Außerdem enthält der Patch ein Dokumentationsupdate für den **Rekursionsfall. Aber für **zu arbeiten, Sie haben die einstellen recursion=TrueSchalter, btw.
Martijn Pieters
19

Es gibt viel Verwirrung zu diesem Thema. Lassen Sie mich sehen, ob ich es klären kann (Python 3.7):

  1. glob.glob('*.txt') :stimmt mit allen Dateien überein, die im aktuellen Verzeichnis mit '.txt' enden
  2. glob.glob('*/*.txt') :gleich wie 1
  3. glob.glob('**/*.txt') :stimmt mit allen Dateien überein, die mit '.txt' enden, nur in den unmittelbaren Unterverzeichnissen , jedoch nicht im aktuellen Verzeichnis
  4. glob.glob('*.txt',recursive=True) :gleich wie 1
  5. glob.glob('*/*.txt',recursive=True) :wie 3
  6. glob.glob('**/*.txt',recursive=True):stimmt mit allen Dateien überein, die im aktuellen Verzeichnis und in allen Unterverzeichnissen mit '.txt' enden

Es ist also am besten, immer anzugeben recursive=True.

Keim
quelle
1
Dies sollte die beste Antwort sein!
Abhik Sarkar
17

Das glob2- Paket unterstützt Platzhalter und ist relativ schnell

code = '''
import glob2
glob2.glob("files/*/**")
'''
timeit.timeit(code, number=1)

Auf meinem Laptop dauert es ungefähr 2 Sekunden, bis > 60.000 Dateipfade übereinstimmen .

Megawac
quelle
9

Sie können Formic mit Python 2.6 verwenden

import formic
fileset = formic.FileSet(include="**/*.txt", directory="C:/Users/sam/Desktop/")

Offenlegung - Ich bin der Autor dieses Pakets.

Andrew Alcock
quelle
4

Hier ist eine angepasste Version, die glob.globähnliche Funktionen ohne Verwendung ermöglicht glob2.

def find_files(directory, pattern='*'):
    if not os.path.exists(directory):
        raise ValueError("Directory not found {}".format(directory))

    matches = []
    for root, dirnames, filenames in os.walk(directory):
        for filename in filenames:
            full_path = os.path.join(root, filename)
            if fnmatch.filter([full_path], pattern):
                matches.append(os.path.join(root, filename))
    return matches

Wenn Sie also die folgende Verzeichnisstruktur haben

tests/files
├── a0
   ├── a0.txt
   ├── a0.yaml
   └── b0
       ├── b0.yaml
       └── b00.yaml
└── a1

Sie können so etwas tun

files = utils.find_files('tests/files','**/b0/b*.yaml')
> ['tests/files/a0/b0/b0.yaml', 'tests/files/a0/b0/b00.yaml']

Ziemlich genau die fnmatchMusterübereinstimmung für den gesamten Dateinamen selbst und nicht nur für den Dateinamen.

Cevaris
quelle
2

configfiles = glob.glob('C:/Users/sam/Desktop/**/*.txt")

Funktioniert nicht in allen Fällen, verwenden Sie stattdessen glob2

configfiles = glob2.glob('C:/Users/sam/Desktop/**/*.txt")
NILESH KUMAR
quelle
2

Wenn Sie das glob2-Paket installieren können ...

import glob2
filenames = glob2.glob("C:\\top_directory\\**\\*.ext")  # Where ext is a specific file extension
folders = glob2.glob("C:\\top_directory\\**\\")

Alle Dateinamen und Ordner:

all_ff = glob2.glob("C:\\top_directory\\**\\**")  
träumen
quelle
2

Wenn Sie Python 3.4+ ausführen, können Sie das pathlibModul verwenden. Die Path.glob()Methode unterstützt das **Muster, dh "dieses Verzeichnis und alle Unterverzeichnisse rekursiv". Es gibt einen Generator zurück, der PathObjekte für alle übereinstimmenden Dateien liefert .

from pathlib import Path
configfiles = Path("C:/Users/sam/Desktop/file1/").glob("**/*.txt")
Eugene Yarmash
quelle
0

Wie von Martijn hervorgehoben, kann glob dies nur über den **in Python 3.5 eingeführten Operator tun . Da das OP explizit nach dem Glob-Modul gefragt hat, wird im Folgenden ein verzögerter Evaluierungsiterator zurückgegeben, der sich ähnlich verhält

import os, glob, itertools

configfiles = itertools.chain.from_iterable(glob.iglob(os.path.join(root,'*.txt'))
                         for root, dirs, files in os.walk('C:/Users/sam/Desktop/file1/'))

Beachten Sie, dass Sie configfilesbei diesem Ansatz jedoch nur einmal iterieren können . Wenn Sie eine echte Liste von Konfigurationsdateien benötigen, die in mehreren Vorgängen verwendet werden können, müssen Sie diese explizit mithilfe von erstellen list(configfiles).

f0xdx
quelle
0

Der Befehl rglobführt eine unendliche Rekursion auf der tiefsten Unterebene Ihrer Verzeichnisstruktur durch. Wenn Sie nur eine Ebene tief möchten, verwenden Sie diese jedoch nicht.

Mir ist klar, dass das OP über die Verwendung von glob.glob sprach. Ich glaube jedoch, dass dies die Absicht beantwortet, alle Unterordner rekursiv zu durchsuchen.

Die rglobFunktion führte kürzlich zu einer 100-fachen Geschwindigkeitssteigerung für einen Datenverarbeitungsalgorithmus, der die Ordnerstruktur als feste Annahme für die Reihenfolge des Datenlesens verwendete. Mit konnten rglobwir jedoch alle Dateien in oder unter einem bestimmten übergeordneten Verzeichnis einmal scannen, ihre Namen in einer Liste (über eine Million Dateien) speichern und dann anhand dieser Liste bestimmen, welche Dateien wir zu einem beliebigen Zeitpunkt öffnen mussten Zeigen Sie in der Zukunft nur auf der Grundlage der Dateinamenskonventionen im Vergleich zu dem Ordner, in dem sie sich befanden.

brethvoice
quelle