Dosyaları özyinelemeli olarak bulmak için glob () nasıl kullanılır?


738

Sahip olduğum şey bu:

glob(os.path.join('src','*.c'))

ama src alt klasörlerinde arama yapmak istiyorum. Böyle bir şey işe yarayacaktır:

glob(os.path.join('src','*.c'))
glob(os.path.join('src','*','*.c'))
glob(os.path.join('src','*','*','*.c'))
glob(os.path.join('src','*','*','*','*.c'))

Ancak bu açıkça sınırlı ve hantaldır.

Yanıtlar:


1355

Python 3.5 ve üzeri

Yeni bir python kullandığınızdan pathlib.Path.rglob, pathlibmodülden kullanmanız gerekir .

from pathlib import Path

for path in Path('src').rglob('*.c'):
    print(path.name)

Pathlib kullanmak istemiyorsanız, sadece şunu kullanın glob.glob, ancak recursiveanahtar kelime parametresini iletmeyi unutmayın .

Eşleşen dosyaların nokta (.) İle başlayan durumlarda; geçerli dizindeki dosyalar veya Unix tabanlı sistemdeki gizli dosyalar os.walkgibi aşağıdaki çözümü kullanın .

Eski Python sürümleri

Eski Python sürümleri için, os.walkbir dizini yinelemeli olarak yürümek ve fnmatch.filterbasit bir ifadeyle eşleştirmek için kullanın:

import fnmatch
import os

matches = []
for root, dirnames, filenames in os.walk('src'):
    for filename in fnmatch.filter(filenames, '*.c'):
        matches.append(os.path.join(root, filename))

3
2.2 yaşından büyük Python os.path.walk()için kullanmak biraz daha zorduros.walk()
John La Rooy

20
@gnibbler Bunun eski bir yorum olduğunu biliyorum, ama benim yorumum sadece insanların os.path.walk()Python 3'te kaldırıldığını ve kaldırıldığını bildirmek .
Pedro Cunha

5
@DevC, bu soruda sorulan özel durumda çalışabilir, ancak 'a * .c' vb.Gibi sorgularla kullanmak isteyen birini hayal etmek kolaydır, bu yüzden mevcut biraz yavaş cevabı tutmaya değer.
Johan Dahlin

2
Değeri için, benim durumumda glob ile 10.000+ dosya bulmak os.walk ile çok daha yavaş, bu nedenle bu nedenle ikinci çözüm ile gitti.
Godsmith

2
Python 3.4 için pathlib.Path('src').glob('**/*.c')çalışmalı.
CivFan

111

Diğer çözümlere benzer, ancak os.walk dosya adlarını zaten listelediğinden glob yerine fnmatch.fnmatch kullanma:

import os, fnmatch


def find_files(directory, pattern):
    for root, dirs, files in os.walk(directory):
        for basename in files:
            if fnmatch.fnmatch(basename, pattern):
                filename = os.path.join(root, basename)
                yield filename


for filename in find_files('src', '*.c'):
    print 'Found C source:', filename

Ayrıca, bir jeneratör kullanmak, tüm dosyaları bulmak ve daha sonra işlemek yerine, her dosyayı bulunduğu gibi işlemenizi sağlar.


3
çünkü 1-gömlekler eğlencelidir:reduce(lambda x, y: x+y, map(lambda (r,_,x):map(lambda f: r+'/'+f, filter(lambda f: fnmatch.fnmatch(f, pattern), x)), os.walk('src/webapp/test_scripts')))
njzk2

1
@ njzk2(os.path.join(root,filename) for root, dirs, files in os.walk(directory) for filename in files if fnmatch.fnmatch(filename, pattern))
Baldrickk

73

Glob modülünü tekrarlayan globbing için ** destekleyecek şekilde değiştirdim, örneğin:

>>> import glob2
>>> all_header_files = glob2.glob('src/**/*.c')

https://github.com/miracle2k/python-glob2/

Kullanıcılarınıza ** sözdizimini kullanma olanağı sağlamak istediğinizde kullanışlıdır ve bu nedenle os.walk () tek başına yeterli değildir.


2
İlk maçı bulduktan sonra bu durağı yapabilir miyiz? Belki de olası her sonucun bir listesini döndürmesini sağlamak yerine bir jeneratör olarak kullanmayı mümkün kılabilir? Ayrıca, bu bir DFS mi yoksa BFS mi? Bence, bir BFS'yi tercih ederim, böylece köke yakın olan dosyalar önce bulunur. Bu modülü yapmak ve GitHub / pip üzerinde sağlamak için +1.
ArtOfWarfare

14
** sözdizimi Python 3.5'teki resmi glob modülüne eklendi.
ArtOfWarfare

@ArtOfWarfare Tamam, iyi. Bu hala <3.5 için yararlıdır.
cs95

1
**Resmi glob modülü ile özyinelemeli globbing etkinleştirmek için glob(path, recursive=True)
şunları yapın

68

Python 3.4'ten başlayarak , joker karakterleri destekleyen yeni pathlib modülündeki sınıflardan glob()birinin yöntemini kullanabilirsiniz . Örneğin:Path**

from pathlib import Path

for file_path in Path('src').glob('**/*.c'):
    print(file_path) # do whatever you need with these files

Güncelleme: Python 3.5 ile başlayarak, aynı sözdizimi tarafından da desteklenir glob.glob().


3
Gerçekten de Python 3.5'te olacak . Python 3.4'te zaten böyle olması gerekiyordu, ancak yanlışlıkla atlandı .
taleinat


Göreli yolları almak için pathlib.PurePath.relative_to komutunu birlikte kullanabileceğinizi unutmayın . Daha fazla bağlam için cevabımı buraya bakın .
pjgranahan

40
import os
import fnmatch


def recursive_glob(treeroot, pattern):
    results = []
    for base, dirs, files in os.walk(treeroot):
        goodfiles = fnmatch.filter(files, pattern)
        results.extend(os.path.join(base, f) for f in goodfiles)
    return results

fnmatchtam olarak aynı desenleri verir glob, bu yüzden bu glob.globçok yakın semantik için mükemmel bir yedek . Yinelemeli bir sürüm (örn. Bir jeneratör), IOW'nin yerine geçmesi glob.iglobönemsiz bir uyarlamadır ( sonunda geri dönmek için tek bir sonuç listesi girmek yieldyerine, yalnızca aradaki sonuçlar extend).


1
recursive_glob(pattern, treeroot='.')Düzenlememde önerdiğim gibi kullanma hakkında ne düşünüyorsun ? Bu şekilde, örneğin recursive_glob('*.txt')sözdizimi olarak sezgisel olarak eşleştirilebilir glob.
Chris Redford

@ChrisRedford, her iki şekilde de oldukça küçük bir sorun olarak görüyorum. Şimdi durduğu fnmatch.filtergibi, kabaca tekli argümanı eşleştirme olasılığı kadar faydalı olan "dosyalar sonra desen" argüman sırasına uymaktadır glob.glob.
Alex Martelli

25

Piton için> = 3.5 kullanabilirsiniz **, recursive=True:

import glob
for x in glob.glob('path/**/*.c', recursive=True):
    print(x)

gösteri


Özyinelemeli ise True, desen ** herhangi bir dosyayla ve sıfır veya daha fazla directoriesve ile eşleşirsubdirectories . Deseni bir a izliyorsa os.sep, sadece dizinler ve subdirectorieseşleşir.


2
Bu, pathlib.Path ('./ path /'). Glob (' * / ') 'den daha iyi çalışır çünkü 0 boyutundaki klasörde de öyle
Charles Walker

20

os.walkÖlçütlerinize uyan dosya adları toplamak için kullanmak istersiniz . Örneğin:

import os
cfiles = []
for root, dirs, files in os.walk('src'):
  for file in files:
    if file.endswith('.c'):
      cfiles.append(os.path.join(root, file))

15

Yuvalanmış liste kavrayışları os.walkve bunun yerine basit sonek eşleşmesi olan bir çözüm glob:

import os
cfiles = [os.path.join(root, filename)
          for root, dirnames, filenames in os.walk('src')
          for filename in filenames if filename.endswith('.c')]

Tek astarla sıkıştırılabilir:

import os;cfiles=[os.path.join(r,f) for r,d,fs in os.walk('src') for f in fs if f.endswith('.c')]

veya bir işlev olarak genelleştirilmiş:

import os

def recursive_glob(rootdir='.', suffix=''):
    return [os.path.join(looproot, filename)
            for looproot, _, filenames in os.walk(rootdir)
            for filename in filenames if filename.endswith(suffix)]

cfiles = recursive_glob('src', '.c')

Tam globstil desenlere ihtiyacınız varsa, Alex'in ve Bruno'nun örneğini takip edebilir ve kullanabilirsiniz fnmatch:

import fnmatch
import os

def recursive_glob(rootdir='.', pattern='*'):
    return [os.path.join(looproot, filename)
            for looproot, _, filenames in os.walk(rootdir)
            for filename in filenames
            if fnmatch.fnmatch(filename, pattern)]

cfiles = recursive_glob('src', '*.c')

7

Son zamanlarda resimlerimi .jpg uzantısıyla kurtarmak zorunda kaldım. Ben photorec koştu ve 4579 dizin 2.2 milyon dosyaları içinde muazzam uzantıları çeşitli kurtardı.Aşağıdaki komut dosyası ile birkaç dakika içinde 50133 dosyaları havin .jpg uzantısı seçebildi:

#!/usr/binenv python2.7

import glob
import shutil
import os

src_dir = "/home/mustafa/Masaüstü/yedek"
dst_dir = "/home/mustafa/Genel/media"
for mediafile in glob.iglob(os.path.join(src_dir, "*", "*.jpg")): #"*" is for subdirectory
    shutil.copy(mediafile, dst_dir)

7

Düşünün pathlib.rglob().

Bu, verilen göreli kalıbın önüne eklenen Path.glob()ile çağrı yapmak gibidir "**/":

import pathlib


for p in pathlib.Path("src").rglob("*.c"):
    print(p)

Ayrıca bkz. @ Taleinat'ın ilgili gönderi ve başka bir yerde benzer bir gönderi .


5

Johan ve Bruno belirtildiği gibi minimum gereksinim için mükemmel çözümler sunar. Sadece yayımlandı formik hangi uygular Ant fileset ve Globs bunları ve daha fazlasını karmaşık senaryoları işleyebilir. İhtiyacınızın bir uygulaması:

import formic
fileset = formic.FileSet(include="/src/**/*.c")
for file_name in fileset.qualified_files():
    print file_name

1
Formic terk edilmiş gibi görünüyor mu ?! Ve Python 3 desteklemez ( bitbucket.org/aviser/formic/issue/12/support-python-3 )
blueyed

5

diğer cevaplara dayanarak, bu benim bir kök dizinde iç içe xml dosyaları alır benim geçerli çalışma uygulamasıdır:

files = []
for root, dirnames, filenames in os.walk(myDir):
    files.extend(glob.glob(root + "/*.xml"))

Gerçekten python ile eğleniyorum :)


3

Bunu sadece glob modülünü kullanarak yapmanın başka bir yolu. Sadece rglob yöntemini bir başlangıç ​​taban dizini ve eşleşecek bir desenle tohumlayın ve eşleşen dosya adlarının bir listesini döndürecektir.

import glob
import os

def _getDirs(base):
    return [x for x in glob.iglob(os.path.join( base, '*')) if os.path.isdir(x) ]

def rglob(base, pattern):
    list = []
    list.extend(glob.glob(os.path.join(base,pattern)))
    dirs = _getDirs(base)
    if len(dirs):
        for d in dirs:
            list.extend(rglob(os.path.join(base,d), pattern))
    return list

3

Python 3.5 ve üstü için

import glob

#file_names_array = glob.glob('path/*.c', recursive=True)
#above works for files directly at path/ as guided by NeStack

#updated version
file_names_array = glob.glob('path/**/*.c', recursive=True)

ayrıca ihtiyacınız olabilir

for full_path_in_src in  file_names_array:
    print (full_path_in_src ) # be like 'abc/xyz.c'
    #Full system path of this would be like => 'path till src/abc/xyz.c'

3
İlk kod satırınız alt dizinlere bakmak için çalışmaz. Ama sadece genişletirseniz benim /**için çalışır, şöyle:file_names_array = glob.glob('src/**/*.c', recursive=True)
NeStack

2

Veya bir liste kavrayışı ile:

 >>> base = r"c:\User\xtofl"
 >>> binfiles = [ os.path.join(base,f) 
            for base, _, files in os.walk(root) 
            for f in files if f.endswith(".jpg") ] 

2

Sadece bunu yaptım .. dosyaları ve dizini hiyerarşik bir şekilde yazdıracak

Ama ben fnmatch veya walk kullanmadım

#!/usr/bin/python

import os,glob,sys

def dirlist(path, c = 1):

        for i in glob.glob(os.path.join(path, "*")):
                if os.path.isfile(i):
                        filepath, filename = os.path.split(i)
                        print '----' *c + filename

                elif os.path.isdir(i):
                        dirname = os.path.basename(i)
                        print '----' *c + dirname
                        c+=1
                        dirlist(i,c)
                        c-=1


path = os.path.normpath(sys.argv[1])
print(os.path.basename(path))
dirlist(path)

2

Bir fnmatch veya normal ifade kullanır:

import fnmatch, os

def filepaths(directory, pattern):
    for root, dirs, files in os.walk(directory):
        for basename in files:
            try:
                matched = pattern.match(basename)
            except AttributeError:
                matched = fnmatch.fnmatch(basename, pattern)
            if matched:
                yield os.path.join(root, basename)

# usage
if __name__ == '__main__':
    from pprint import pprint as pp
    import re
    path = r'/Users/hipertracker/app/myapp'
    pp([x for x in filepaths(path, re.compile(r'.*\.py$'))])
    pp([x for x in filepaths(path, '*.py')])

2

Önerilen cevaplara ek olarak, bunu tembel nesil ve liste anlama sihriyle de yapabilirsiniz:

import os, glob, itertools

results = itertools.chain.from_iterable(glob.iglob(os.path.join(root,'*.c'))
                                               for root, dirs, files in os.walk('src'))

for f in results: print(f)

Tek bir satıra sığmanın ve bellekte gereksiz listelerden kaçınmanın yanı sıra, aynı zamanda ** operatörüne benzer bir şekilde kullanabileceğiniz hoş bir yan etkiye sahiptir, örneğin, os.path.join(root, 'some/path/*.c')tüm .c dosyalarını almak için kullanabilirsiniz . bu yapıya sahip src alt dizinleri.


2

Bu Python 2.7 üzerinde çalışan bir koddur. Donanımlarımın bir parçası olarak, live-appName.properties ile işaretlenen yapılandırma dosyalarını appName.properties'e taşıyacak bir komut dosyası yazmam gerekiyordu. Live-appName.xml gibi başka uzantı dosyaları da olabilir.

Aşağıda verilen dizinlerdeki (iç içe düzey) dosyaları bulan ve daha sonra gerekli dosya adına yeniden adlandıran (taşıyan) bir çalışma kodu aşağıdadır.

def flipProperties(searchDir):
   print "Flipping properties to point to live DB"
   for root, dirnames, filenames in os.walk(searchDir):
      for filename in fnmatch.filter(filenames, 'live-*.*'):
        targetFileName = os.path.join(root, filename.split("live-")[1])
        print "File "+ os.path.join(root, filename) + "will be moved to " + targetFileName
        shutil.move(os.path.join(root, filename), targetFileName)

Bu işlev bir ana komut dosyasından çağrılır

flipProperties(searchDir)

Umarım bu benzer sorunlarla mücadele eden birine yardımcı olur.


1

Johan Dahlin'in cevabının fnmatch olmadan basitleştirilmiş versiyonu .

import os

matches = []
for root, dirnames, filenames in os.walk('src'):
  matches += [os.path.join(root, f) for f in filenames if f[-2:] == '.c']

1

İşte bir dizinde ve tüm alt dizinlerde özyinelemeli olarak birden fazla dosya uzantısı aramak için liste kavrama kullanarak benim çözüm :

import os, glob

def _globrec(path, *exts):
""" Glob recursively a directory and all subdirectories for multiple file extensions 
    Note: Glob is case-insensitive, i. e. for '\*.jpg' you will get files ending
    with .jpg and .JPG

    Parameters
    ----------
    path : str
        A directory name
    exts : tuple
        File extensions to glob for

    Returns
    -------
    files : list
        list of files matching extensions in exts in path and subfolders

    """
    dirs = [a[0] for a in os.walk(path)]
    f_filter = [d+e for d in dirs for e in exts]    
    return [f for files in [glob.iglob(files) for files in f_filter] for f in files]

my_pictures = _globrec(r'C:\Temp', '\*.jpg','\*.bmp','\*.png','\*.gif')
for f in my_pictures:
    print f

0
import sys, os, glob

dir_list = ["c:\\books\\heap"]

while len(dir_list) > 0:
    cur_dir = dir_list[0]
    del dir_list[0]
    list_of_files = glob.glob(cur_dir+'\\*')
    for book in list_of_files:
        if os.path.isfile(book):
            print(book)
        else:
            dir_list.append(book)

0

Bu gönderideki en iyi yanıtı değiştirdim .. ve son zamanlarda belirli bir dizindeki (searchdir) ve altındaki alt dizinlerdeki tüm dosyalar arasında döngü yapacak olan bu komut dosyasını oluşturdum ... ve dosya adı, rootdir, değiştirilme tarihi / oluşturma tarihi ve boyut.

Umarım bu birine yardımcı olur ... ve onlar dizini yürüyüp fileinfo alabilirler.

import time
import fnmatch
import os

def fileinfo(file):
    filename = os.path.basename(file)
    rootdir = os.path.dirname(file)
    lastmod = time.ctime(os.path.getmtime(file))
    creation = time.ctime(os.path.getctime(file))
    filesize = os.path.getsize(file)

    print "%s**\t%s\t%s\t%s\t%s" % (rootdir, filename, lastmod, creation, filesize)

searchdir = r'D:\Your\Directory\Root'
matches = []

for root, dirnames, filenames in os.walk(searchdir):
    ##  for filename in fnmatch.filter(filenames, '*.c'):
    for filename in filenames:
        ##      matches.append(os.path.join(root, filename))
        ##print matches
        fileinfo(os.path.join(root, filename))

0

Burada, deseni yalnızca temel dosya adıyla değil, tam yolla eşleştirecek bir çözüm var.

fnmatch.translateBir glob tarzı deseni normal bir ifadeye dönüştürmek için kullanır , bu daha sonra dizini yürürken bulunan her dosyanın tam yoluyla eşleştirilir.

re.IGNORECASEisteğe bağlıdır, ancak dosya sisteminin kendisi büyük / küçük harf duyarlı olmadığından Windows'ta istenir. (Normal ifadeyi derleme zahmetine girmedim çünkü dokümanlar dahili olarak önbelleğe alınması gerektiğini gösteriyor.)

import fnmatch
import os
import re

def findfiles(dir, pattern):
    patternregex = fnmatch.translate(pattern)
    for root, dirs, files in os.walk(dir):
        for basename in files:
            filename = os.path.join(root, basename)
            if re.search(patternregex, filename, re.IGNORECASE):
                yield filename

0

Büyük dizinlerde hızlı çalışan python 2.x için bir çözüme ihtiyacım vardı . Ben bununla bitirmek:

import subprocess
foundfiles= subprocess.check_output("ls src/*.c src/**/*.c", shell=True)
for foundfile in foundfiles.splitlines():
    print foundfile

lsEşleşen bir dosya bulamaması durumunda bazı özel durum işlemlerine ihtiyacınız olabileceğini unutmayın .


Sadece ls src/**/*.cglobstar seçeneği etkinse ( shopt -s globstar) işe yaradığını fark ettim - ayrıntılar için bu cevaba bakın.
Roman
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.