Jak mogę przeszukiwać podfoldery za pomocą modułu glob.glob?

107

Chcę otworzyć serię podfolderów w folderze i znaleźć kilka plików tekstowych oraz wydrukować kilka wierszy plików tekstowych. Używam tego:

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

Ale to nie może również uzyskać dostępu do podfolderów. Czy ktoś wie, jak mogę użyć tego samego polecenia, aby uzyskać dostęp również do podfolderów?

UżytkownikRRR
źródło

Odpowiedzi:

163

W Pythonie 3.5 i nowszych użyj nowej **/funkcji rekurencyjnej :

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

Kiedy recursivejest ustawiona, **następuje separator ścieżki dopasowujący 0 lub więcej podkatalogów.

We wcześniejszych wersjach Pythona glob.glob()nie można rekurencyjnie wyświetlać plików w podkatalogach.

W takim przypadku użyłbym zamiast tego w os.walk()połączeniu z fnmatch.filter():

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

Spowoduje to rekurencyjne przejrzenie katalogów i zwrócenie wszystkich bezwzględnych ścieżek do pasujących .txtplików. W tym konkretnym przypadku fnmatch.filter()może to być przesada, możesz również użyć .endswith()testu:

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
źródło
3
Widzę, że: glob.glob ('/ ścieżka do katalogu / * / *. Txt ") działa dla mnie. Jest to zasadniczo przy użyciu reguły powłoki Unix.
Surya
7
@ User123: to nie wyświetla rekursywnie katalogów . Wyświetlasz wszystkie pliki tekstowe o jednym poziomie głębokości , ale nie w dalszych podkatalogach ani nawet bezpośrednio w path to directory.
Martijn Pieters
1
Nie jest to do końca powiązane, ale dlaczego ustawienie recursive=Falserazem z **/ funkcjonalnością nie dostarcza listy plików tylko w danym folderze, a raczej w jego elementach podrzędnych?
Dr_Zaszuś
@ Dr_Zaszuś: przepraszam? **/podaje listę nazw katalogów w bieżącym katalogu roboczym, ponieważ wzorzec kończy się na /, a z recursive=Falsetobą w zasadzie masz podwójne *, pasujące tak samo jak */, tylko mniej wydajne.
Martijn Pieters
@ Dr_Zaszuś: użyj, */*jeśli potrzebujesz wszystkich plików we wszystkich podkatalogach.
Martijn Pieters
22

Aby znaleźć pliki w bezpośrednich podkatalogach:

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

W przypadku wersji rekurencyjnej, która przechodzi przez wszystkie podkatalogi, można użyć **i przekazać recursive=True od Pythona 3.5 :

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

Listy zwrotów obu wywołań funkcji. Możesz użyć glob.iglob()do zwrócenia ścieżek jedna po drugiej. Lub użyjpathlib :

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

Obie metody zwracają iteratory (ścieżki można pobierać pojedynczo).

jfs
źródło
Tak, zrozumiałem to; ale nie spodziewałem glob()się również obsługi wzorców w katalogach.
Martijn Pieters
Komentarz usunięty, teraz widzę, że zrobił złe wrażenie; poza tym łatka zawiera aktualizację dokumentacji dla **przypadku rekursji. Ale dla **do pracy, mają ustawić recursion=Trueprzełącznik, btw.
Martijn Pieters
20

W tym temacie jest wiele nieporozumień. Zobaczmy, czy mogę to wyjaśnić (Python 3.7):

  1. glob.glob('*.txt') :dopasowuje wszystkie pliki kończące się na „.txt” w bieżącym katalogu
  2. glob.glob('*/*.txt') :tak samo jak 1
  3. glob.glob('**/*.txt') :dopasowuje wszystkie pliki kończące się na „.txt” tylko w bezpośrednich podkatalogach , ale nie w bieżącym katalogu
  4. glob.glob('*.txt',recursive=True) :tak samo jak 1
  5. glob.glob('*/*.txt',recursive=True) :tak samo jak 3
  6. glob.glob('**/*.txt',recursive=True):dopasowuje wszystkie pliki kończące się na „.txt” w bieżącym katalogu i we wszystkich podkatalogach

Dlatego najlepiej zawsze określać recursive=True.

zalążek
źródło
1
To powinna być najlepsza odpowiedź!
Abhik Sarkar
17

Glob2 Pakiet obsługuje dzikie karty i jest dość szybki

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

Na moim laptopie dopasowanie > 60 000 ścieżek plików zajmuje około 2 sekund .

megawac
źródło
9

Możesz używać Formic z Pythonem 2.6

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

Ujawnienie - jestem autorem tego pakietu.

Andrew Alcock
źródło
4

Oto dostosowana wersja, która umożliwia glob.globpodobną funkcjonalność bez użycia 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

Więc jeśli masz następującą strukturę dir

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

Możesz zrobić coś takiego

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

Prawie fnmatchdopasowanie wzorca na całej nazwie pliku, a nie tylko na nazwie pliku.

cevaris
źródło
2

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

Nie działa we wszystkich przypadkach, zamiast tego użyj glob2

configfiles = glob2.glob('C:/Users/sam/Desktop/**/*.txt")
NILESH KUMAR
źródło
2

Jeśli możesz zainstalować pakiet glob2 ...

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

Wszystkie nazwy plików i foldery:

all_ff = glob2.glob("C:\\top_directory\\**\\**")  
dreab
źródło
2

Jeśli używasz Pythona 3.4+, możesz użyć pathlibmodułu. Path.glob()Metoda obsługuje **wzorzec, co oznacza „tego katalogu i wszystkich podkatalogów rekurencyjnie”. Zwraca generator generujący Pathobiekty dla wszystkich pasujących plików.

from pathlib import Path
configfiles = Path("C:/Users/sam/Desktop/file1/").glob("**/*.txt")
Eugene Yarmash
źródło
0

Jak zauważył Martijn, glob może to zrobić tylko za pośrednictwem **operatora wprowadzonego w Pythonie 3.5. Ponieważ program operacyjny wyraźnie poprosił o moduł glob, poniższy komunikat zwróci leniwy iterator oceny, który zachowuje się podobnie

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/'))

Zauważ, że możesz powtórzyć configfilesto podejście tylko raz . Jeśli potrzebujesz prawdziwej listy plików konfiguracyjnych, których można użyć w wielu operacjach, musisz utworzyć ją jawnie za pomocą list(configfiles).

f0xdx
źródło
0

Polecenie rglobwykona nieskończoną rekursję na najgłębszym poziomie struktury katalogów. Jeśli chcesz mieć tylko jeden poziom głębokości, nie używaj go.

Zdaję sobie sprawę, że OP mówił o używaniu glob.glob. Uważam jednak, że to odpowiada intencji, która polega na rekurencyjnym przeszukiwaniu wszystkich podfolderów.

rglobFunkcja wyprodukowała 100x wzrost prędkości dla algorytmu obróbki danych, który został z wykorzystaniem struktury katalogów jako stała założeniu na celu odczytu danych. Jednak dzięki temu, rglobże byliśmy w stanie raz wykonać jedno skanowanie wszystkich plików w określonym katalogu nadrzędnym lub poniżej, zapisać ich nazwy na liście (ponad milion plików), a następnie użyć tej listy, aby określić, które pliki musimy otworzyć w dowolnym w przyszłości w oparciu o konwencje nazewnictwa plików w porównaniu z tym, w którym folderze się znajdują.

brthvoice
źródło