glob()を使用してファイルを再帰的に検索する方法は?


738

これは私が持っているものです:

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

srcのサブフォルダーを検索したいのですが。このような何かがうまくいくでしょう:

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

しかし、これは明らかに限定的で不格好です。

回答:


1355

Python 3.5以降

新しいpythonを使用pathlib.Path.rglobしているので、pathlibモジュールから使用する必要があります。

from pathlib import Path

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

pathlibを使用したくない場合は、を使用しますglob.globが、recursivekeywordパラメータを渡すことを忘れないでください。

一致するファイルがドット(。)で始まる場合。現在のディレクトリのファイルやUnixベースのシステムの隠しファイルのように、os.walk以下のソリューションを使用します。

古いPythonバージョン

古いバージョンのPythonでは、を使用os.walkしてディレクトリを再帰的にウォークfnmatch.filterし、単純な式と照合します。

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
Python用の古いが2.2よりもそこにあるos.path.walk()よりも、使用にはもう少し手間のかかるあるos.walk()
ジョン・ラRooy

20
@gnibbler私は古いコメントですけど、私のコメントは、人々がそれを知っているようにだけでos.path.walk()廃止されており、Pythonの3で削除されました
ペドロ・クーニャ

5
この質問で尋ねられた特定のケースで機能する可能性のある@DevCですが、「a * .c」などのクエリでそれを使用したい人を想像するのは簡単なので、現在のやや遅い答えを維持することは価値があると思います。
Johan Dahlin、2014年

2
それだけの価値があるのですが、私の場合、グロブを使用して10,000以上のファイルを見つけるのはos.walkを使用するよりもはるかに遅くなったため、その理由で後者のソリューションを採用しました。
Godsmith

2
Python 3.4の場合、動作するpathlib.Path('src').glob('**/*.c')はずです。
CivFan

111

他のソリューションと同様ですが、os.walkがすでにファイル名をリストしているため、globの代わりにfnmatch.fnmatchを使用します。

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

また、ジェネレータを使用すると、すべてのファイルを見つけて処理するのではなく、各ファイルを見つけたときに処理することができます。


3
1ライナーは楽しいからです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モジュールを変更しました。例:

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

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

**構文を使用する機能をユーザーに提供したい場合に役立ちます。したがって、os.walk()だけでは十分ではありません。


2
最初の一致が見つかったら、これを停止できますか?可能性のあるすべての結果のリストを返すのではなく、ジェネレータとして使用できるようにするのでしょうか?また、これはDFSまたはBFSですか?ルートの近くにあるファイルが最初に見つかるように、私はBFSをはるかに好みます。このモジュールを作成し、GitHub / pipで提供するための+1。
ArtOfWarfare 2014

14
**構文は、Python 3.5の公式のglobモジュールに追加されました。
ArtOfWarfare 2015年

@ArtOfWarfareわかりました。これは、3.5未満の場合にも役立ちます。
cs95 2017

1
アクティブ再帰グロブに使用した**公式グロブモジュールと、実行しますglob(path, recursive=True)
winklerrr

68

Python 3.4 以降では、ワイルドカードをサポートする新しいpathlibモジュールのクラスglob()の1つのメソッドを使用できます。例えば:Path**

from pathlib import Path

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

更新: Python 3.5以降、同じ構文がでもサポートされていglob.glob()ます。


3
確かに、それはPython 3.5に含まれる予定です。Python 3.4ではすでにそうであるはずでしたが、誤って省略されました
taleinat 2015


pathlib.PurePath.relative_toを組み合わせて使用して相対パスを取得することもできます。詳細については、こちらの回答をご覧ください。
pjgranahan 2017

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

fnmatchはとまったく同じパターンを提供するglobので、これはglob.glob非常に厳密なセマンティクスを持つの優れた代替品です。反復バージョン(例えば、ジェネレーター)、IOWの代わりglob.iglobは、簡単な適応です(単一の結果リストを最後に返すのではyieldなく、途中の結果だけですextend)。


1
recursive_glob(pattern, treeroot='.')編集で提案したように使用することについてどう思いますか?このようにして、たとえばrecursive_glob('*.txt')の構文と同じように呼び出すことができ、の構文と直感的に一致しますglob
Chris Redford

@ChrisRedford、どちらにせよ、それはかなり小さな問題だと思います。現在のところ、「files then pattern」引数の順序fnmatch.filterに一致しますglob.glob。これは、単一引数に一致する可能性とほぼ同じくらい便利です。
Alex Martelli、2015年

25

Python用> = 3.5あなたが使用することができ**recursive=True

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

デモ


再帰的であるTrue場合、パターン** はすべてのファイルと0個以上directoriesと一致しますsubdirectories。パターンの後にが続く場合はos.sep、ディレクトリとsubdirectories一致のみです。


2
これは、pathlib.Path( './ path /')。glob( ' * / ')よりも適切に機能します。これは、サイズ0のフォルダーでも同様であるためです
Charles Walker

20

を使用os.walkして、基準に一致するファイル名を収集します。例えば:

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

ネストされたリスト内包表記を使用した解決策を次に示しos.walkます。代わりに単純なサフィックスマッチングを使用し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')]

ワンライナーに圧縮できます:

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

または関数として一般化:

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

完全なglobスタイルパターンが必要な場合は、AlexとBrunoの例に従い、次を使用できます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

最近、拡張子.jpgを付けて写真を復元する必要がありました。私はphotorecを実行し、膨大な種類の拡張子を持つ4579ディレクトリ内の220万のファイルを回復しました。以下のスクリプトを使用して、50133ファイルのhavin .jpg拡張子を数分で選​​択できました。

#!/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

考えてくださいpathlib.rglob()

これは、呼び出しのようなものであるPath.glob()"**/"指定した相対的なパターンの前に追加しました:

import pathlib


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

こちらの@taleinatの関連する投稿と他の場所の同様の投稿もご覧ください。


5

述べたように、ヨハンとブルーノは最小限の要件で優れたソリューションを提供します。私は、この複雑なシナリオを処理できるAnt FileSetとGlobsを実装するFormicをリリースしました。要件の実装は次のとおりです。

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

1
ギミックは放棄されているようです!?そして、それは、Python 3をサポートしていません(bitbucket.org/aviser/formic/issue/12/support-python-3
blueyed

5

他の回答に基づいて、これは私の現在機能している実装であり、ルートディレクトリ内のネストされたxmlファイルを取得します。

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

私は本当にpythonを楽しんでいます:)


3

globモジュールだけを使用する別の方法。開始ベースディレクトリと一致するパターンをrglobメソッドにシードするだけで、一致するファイル名のリストが返されます。

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以降の場合

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)

さらに必要になるかもしれません

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
コードの最初の行は、サブディレクトリを調べるためには機能しません。しかし、それだけで拡張すると、次の/**ように機能しますfile_names_array = glob.glob('src/**/*.c', recursive=True)
。– NeStack

2

またはリスト内包表記:

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

これを作成しました..ファイルとディレクトリを階層的に出力します

しかし、私はfnmatchやwalkを使用しませんでした

#!/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

これはfnmatchまたは正規表現を使用します。

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

提案された回答に加えて、遅延生成とリスト理解魔法を使用してこれを行うことができます:

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)

これは、1行に収めてメモリ内の不要なリストを回避するだけでなく、**演算子と同様の方法で使用できるというすばらしい副作用もあります。たとえば、os.path.join(root, 'some/path/*.c')すべての.cファイルをすべて取得するために使用できます。この構造を持つsrcのサブディレクトリ。


2

これはPython 2.7で動作するコードです。開発作業の一環として、live-appName.propertiesでマークされた構成ファイルをappName.propertiesに移動するスクリプトを作成する必要がありました。live-appName.xmlのような他の拡張ファイルが存在する可能性があります。

以下は、このための作業コードです。これは、指定されたディレクトリ(ネストされたレベル)でファイルを見つけ、必要なファイル名に名前を変更(移動)します。

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)

この関数はメインスクリプトから呼び出されます

flipProperties(searchDir)

これが同様の問題に苦しんでいる誰かを助けることを願っています。


1

fnmatchなしのJohan Dahlinの回答の簡略版。

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

リスト内包表記を使用してディレクトリとすべてのサブディレクトリで複数のファイル拡張子を再帰的に検索する私の解決策は次のとおりです。

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

私はこの投稿のトップアンサーを変更しました。最近、このスクリプトを作成しました。このスクリプトは、特定のディレクトリ(searchdir)とその下のサブディレクトリにあるすべてのファイルをループし、ファイル名、rootdir、変更/作成日、およびサイズ。

これが誰かを助けることを願っています...そして彼らはディレクトリを歩いてファイル情報を得ることができます。

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

これは、ベースファイル名だけでなく、フルパスに対してパターンを照合するソリューションです。

これはfnmatch.translate、globスタイルのパターンを正規表現に変換するために使用します。正規表現は、ディレクトリを歩くときに見つかった各ファイルの完全パスと照合されます。

re.IGNORECASEオプションですが、Windowsではファイルシステム自体で大文字と小文字が区別されないため、これが望ましいです。(ドキュメントは内部的にキャッシュする必要があることをドキュメントが示しているので、私は正規表現をコンパイルする気になりませんでした。)

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

大規模なディレクトリで高速に動作するpython 2.xのソリューションが必要でした。 私はこれで終わります:

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

ls一致するファイルが見つからない場合は、例外処理が必要になる場合があることに注意してください。


ls src/**/*.cはglobstarオプションが有効になっている場合にのみ機能することを認識しました(shopt -s globstar)-詳細についてはこの回答を参照してください。
ローマ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.