ฉันจะค้นหาโฟลเดอร์ย่อยโดยใช้โมดูล glob.glob ได้อย่างไร


107

ฉันต้องการเปิดชุดโฟลเดอร์ย่อยในโฟลเดอร์และค้นหาไฟล์ข้อความและพิมพ์ไฟล์ข้อความบางบรรทัด ฉันใช้สิ่งนี้:

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

แต่ไม่สามารถเข้าถึงโฟลเดอร์ย่อยได้เช่นกัน มีใครรู้บ้างว่าฉันสามารถใช้คำสั่งเดียวกันเพื่อเข้าถึงโฟลเดอร์ย่อยได้อย่างไร


คำตอบ:


163

ใน Python 3.5 และใหม่กว่าให้ใช้**/ฟังก์ชันการเรียกซ้ำใหม่:

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

เมื่อrecursiveตั้งค่าแล้ว**ตามด้วยตัวคั่นเส้นทางตรงกับไดเร็กทอรีย่อย 0 หรือมากกว่า

ใน Python เวอร์ชันก่อนหน้าglob.glob()ไม่สามารถแสดงรายการไฟล์ในไดเร็กทอรีย่อยแบบวนซ้ำได้

ในกรณีนี้ฉันจะใช้os.walk()ร่วมกับ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')]

สิ่งนี้จะนำไดเรกทอรีของคุณแบบวนซ้ำและส่งคืนชื่อพา ธ สัมบูรณ์ทั้งหมดไปยัง.txtไฟล์ที่ตรงกัน ในกรณีเฉพาะนี้fnmatch.filter()อาจใช้งานมากเกินไปคุณสามารถใช้การ.endswith()ทดสอบ:

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

4
ฉันเห็น: glob.glob ('/ path to directory / * / *. txt ")ทำงานให้ฉันนี่คือพื้นฐานโดยใช้กฎเปลือก Unix
Surya

8
@ User123: ที่ไม่ไดเรกทอรีรายชื่อซ้ำ คุณกำลังแสดงรายการไฟล์ข้อความทั้งหมดในระดับลึกแต่ไม่ได้อยู่ในไดเรกทอรีย่อยเพิ่มเติมหรือแม้แต่ในpath to directoryไฟล์.
Martijn Pieters

2
สิ่งนี้ไม่เกี่ยวข้องกันอย่างสมบูรณ์ แต่เหตุใดการตั้งค่าrecursive=Falseร่วมกับ**/ ฟังก์ชันจึงไม่ได้จัดเตรียมรายการไฟล์ไว้ในโฟลเดอร์ที่กำหนด แต่อยู่ในกลุ่มย่อย
Dr_Zaszuś

@ Dr_Zaszuś: ขอโทษ? **/ให้รายชื่อไดเร็กทอรีในไดเร็กทอรีการทำงานปัจจุบันเนื่องจากรูปแบบลงท้าย/ด้วยและrecursive=Falseโดยพื้นฐานแล้วคุณจะมีคู่ที่*ตรงกันเหมือนกับ*/เพียง แต่มีประสิทธิภาพน้อยกว่า
Martijn Pieters

1
@ Dr_Zaszuś: ใช้*/*ถ้าคุณต้องการไฟล์ทั้งหมดในไดเรกทอรีย่อยทั้งหมด
Martijn Pieters

22

ในการค้นหาไฟล์ในไดเร็กทอรีย่อยทันที:

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

สำหรับเวอร์ชันเรียกซ้ำที่ข้ามไดเร็กทอรีย่อยทั้งหมดคุณสามารถใช้**และส่งผ่านrecursive=True ตั้งแต่ Python 3.5 :

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

ฟังก์ชันทั้งสองเรียกรายการส่งคืน คุณสามารถใช้glob.iglob()เพื่อส่งกลับเส้นทางทีละรายการ หรือใช้pathlib :

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

ทั้งสองวิธีส่งคืนตัวทำซ้ำ (คุณสามารถรับเส้นทางทีละรายการ)


ใช่ฉันเข้าใจแล้ว แต่ฉันไม่ได้คาดหวังว่าglob()จะรองรับรูปแบบในไดเรกทอรีด้วย
Martijn Pieters

ความคิดเห็นถูกลบฉันเห็นว่าตอนนี้แสดงผลผิด นอกจากนี้โปรแกรมแก้ไขยังมีการอัปเดตเอกสารสำหรับ**กรณีการเรียกซ้ำ แต่สำหรับ**การทำงานคุณต้องตั้งrecursion=Trueสวิตช์ btw
Martijn Pieters

20

มีความสับสนมากมายในหัวข้อนี้ ให้ฉันดูว่าฉันสามารถชี้แจงได้หรือไม่ (Python 3.7):

  1. glob.glob('*.txt') :จับคู่ไฟล์ทั้งหมดที่ลงท้ายด้วย ".txt" ในไดเร็กทอรีปัจจุบัน
  2. glob.glob('*/*.txt') :เช่นเดียวกับ 1
  3. glob.glob('**/*.txt') :จับคู่ไฟล์ทั้งหมดที่ลงท้ายด้วย '.txt' ในไดเร็กทอรีย่อยทันทีเท่านั้นแต่ไม่ใช่ในไดเร็กทอรีปัจจุบัน
  4. glob.glob('*.txt',recursive=True) :เช่นเดียวกับ 1
  5. glob.glob('*/*.txt',recursive=True) :เช่นเดียวกับ 3
  6. glob.glob('**/*.txt',recursive=True):จับคู่ไฟล์ทั้งหมดที่ลงท้ายด้วย ".txt" ในไดเร็กทอรีปัจจุบันและในไดเร็กทอรีย่อยทั้งหมด

ดังนั้นควรระบุเสมอ recursive=True.


1
นี่น่าจะเป็นคำตอบอันดับต้น ๆ !
Abhik Sarkar

17

glob2แพคเกจรองรับการ์ดป่าและเป็นเหตุผลได้อย่างรวดเร็ว

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

แล็ปท็อปของฉันมันจะใช้เวลาประมาณ 2 วินาทีเพื่อให้ตรงกับ> 60,000 เส้นทางแฟ้ม



4

นี่เป็นรุ่นที่ดัดแปลงที่ช่วยให้การทำงานเช่นโดยไม่ต้องใช้glob.globglob2

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

ดังนั้นหากคุณมีโครงสร้าง dir ต่อไปนี้

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

คุณสามารถทำสิ่งนี้ได้

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

fnmatchรูปแบบค่อนข้างตรงกับชื่อไฟล์ทั้งหมดมากกว่าชื่อไฟล์เท่านั้น


2

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

ใช้ไม่ได้กับทุกกรณีให้ใช้ glob2 แทน

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

2

หากคุณสามารถติดตั้งแพ็คเกจ glob2 ...

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

ชื่อไฟล์และโฟลเดอร์ทั้งหมด:

all_ff = glob2.glob("C:\\top_directory\\**\\**")  

2

หากคุณใช้ Python 3.4+ คุณสามารถใช้pathlibโมดูลนี้ได้ Path.glob()วิธีการสนับสนุน**รูปแบบซึ่งหมายถึง“ไดเรกทอรีนี้และไดเรกทอรีย่อยทั้งหมดซ้ำ” ส่งคืนเครื่องกำเนิดไฟฟ้าที่ให้Pathวัตถุสำหรับไฟล์ที่ตรงกันทั้งหมด

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

0

ดังที่ Martijn ชี้ให้เห็นว่า glob สามารถทำได้ผ่านตัว**ดำเนินการที่แนะนำใน Python 3.5 เท่านั้น เนื่องจาก OP ขอโมดูล glob อย่างชัดเจนสิ่งต่อไปนี้จะส่งคืนตัววนซ้ำการประเมินที่ขี้เกียจซึ่งทำงานในลักษณะเดียวกัน

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

โปรดทราบว่าคุณสามารถทำซ้ำได้เพียงครั้งเดียวconfigfilesในแนวทางนี้ หากคุณต้องการรายการ configfiles จริงที่สามารถใช้ในการดำเนินการหลายรายการคุณจะต้องสร้างสิ่งนี้อย่างชัดเจนโดยใช้list(configfiles).


0

คำสั่งrglobจะทำการเรียกซ้ำแบบไม่สิ้นสุดในระดับย่อยที่ลึกที่สุดของโครงสร้างไดเร็กทอรีของคุณ อย่างไรก็ตามหากคุณต้องการลึกเพียงระดับเดียวอย่าใช้มัน

ฉันรู้ว่า OP กำลังพูดถึงการใช้ glob.glob อย่างไรก็ตามฉันเชื่อว่าสิ่งนี้ตอบโจทย์เจตนาซึ่งก็คือการค้นหาโฟลเดอร์ย่อยทั้งหมดแบบวนซ้ำ

rglobฟังก์ชั่นเมื่อเร็ว ๆ นี้การผลิตที่เพิ่มขึ้น 100x ความเร็วสำหรับขั้นตอนวิธีการประมวลผลข้อมูลซึ่งใช้โครงสร้างโฟลเดอร์เป็นสมมติฐานคงที่สำหรับการสั่งซื้อของการอ่านข้อมูล อย่างไรก็ตามด้วยการที่rglobเราสามารถทำการสแกนครั้งเดียวผ่านไฟล์ทั้งหมดที่หรือด้านล่างไดเร็กทอรีหลักที่ระบุบันทึกชื่อลงในรายการ (มากกว่าหนึ่งล้านไฟล์) จากนั้นใช้รายการนั้นเพื่อกำหนดไฟล์ที่เราต้องการเปิดที่ใดก็ได้ ชี้ไปในอนาคตตามหลักการตั้งชื่อไฟล์เทียบกับโฟลเดอร์ที่อยู่ในนั้นเท่านั้น


0

คุณสามารถใช้ฟังก์ชันglob.glob()หรือglob.iglob()โดยตรงจากโมดูล glob เพื่อดึงเส้นทางแบบวนซ้ำจากภายในไดเร็กทอรี / ไฟล์และไดเร็กทอรีย่อย / ไฟล์ย่อย

ไวยากรณ์:

glob.glob(pathname, *, recursive=False) # pathname = '/path/to/the/directory' or subdirectory
glob.iglob(pathname, *, recursive=False)

ในตัวอย่างของคุณเป็นไปได้ที่จะเขียนดังนี้:


import glob
import os

configfiles = [f for f in glob.glob("C:/Users/sam/Desktop/*.txt")]

for f in configfiles:
    print(f'Filename with path: {f}')
    print(f'Only filename: {os.path.basename(f)}')
    print(f'Filename without extensions: {os.path.splitext(os.path.basename(f))[0]}')

เอาท์พุต:

Filename with path: C:/Users/sam/Desktop/test_file.txt
Only filename: test_file.txt
Filename without extensions: test_file

วิธีใช้: เอกสารสำหรับos.path.splitextและเอกสารสำหรับos.path.basename.

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.