os.walk โดยไม่ต้องขุดเข้าไปในไดเรกทอรีด้านล่าง


103

ฉันจะ จำกัดos.walkให้ส่งคืนเฉพาะไฟล์ในไดเร็กทอรีที่ฉันให้ไว้ได้อย่างไร

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        for f in files:
            if os.path.splitext(f)[1] in whitelist:
                outputList.append(os.path.join(root, f))
            else:
                self._email_to_("ignore")
    return outputList

2
อีกกรณีหนึ่งที่แนวทางที่เป็นไปได้มากมายและคำเตือนทั้งหมดที่ไปกับพวกเขาแนะนำว่าควรเพิ่มฟังก์ชันนี้ลงในไลบรารีมาตรฐาน Python
antred

files_with_full_path = [f.path for f in os.scandir(dir) if f.is_file()]. ในกรณีที่คุณต้องการเพียงชื่อไฟล์ที่ใช้f.nameแทนf.path. นี่คือวิธีที่เร็วที่สุดและเร็วกว่าที่ใด ๆwalkหรือlistdirดูstackoverflow.com/a/40347279/2441026
user136036

คำตอบ:


105

ใช้ walklevelฟังก์ชัน

import os

def walklevel(some_dir, level=1):
    some_dir = some_dir.rstrip(os.path.sep)
    assert os.path.isdir(some_dir)
    num_sep = some_dir.count(os.path.sep)
    for root, dirs, files in os.walk(some_dir):
        yield root, dirs, files
        num_sep_this = root.count(os.path.sep)
        if num_sep + level <= num_sep_this:
            del dirs[:]

มันใช้งานได้เหมือนos.walkกัน แต่คุณสามารถส่งผ่านlevelพารามิเตอร์ที่ระบุว่าการเรียกซ้ำจะไปลึกแค่ไหน


3
ฟังก์ชั่นนี้ "เดิน" ไปตามโครงสร้างทั้งหมดแล้วลบรายการที่อยู่ด้านล่างจุดใดจุดหนึ่งหรือไม่? หรือมีอะไรที่ฉลาดกว่าเกิดขึ้น? ฉันไม่แน่ใจด้วยซ้ำว่าจะตรวจสอบรหัสนี้อย่างไร --python เริ่มต้น
คณิตศาสตร์คลิก

1
@mathtick: เมื่อพบไดเร็กทอรีบางรายการในหรือต่ำกว่าระดับที่ต้องการไดเร็กทอรีย่อยทั้งหมดจะถูกลบออกจากรายการไดเร็กทอรีย่อยเพื่อค้นหาต่อไป ดังนั้นพวกเขาจะไม่ถูก "เดิน"
nosklo

2
ฉันเพิ่ง +1 สิ่งนี้เพราะฉันกำลังดิ้นรนกับวิธี "ลบ" dirs ฉันลองdirs = []แล้วdirs = Noneแต่มันไม่ได้ผล map(dirs.remove, dirs)ใช้งานได้ แต่มีการพิมพ์ข้อความ "[ไม่มี]" ที่ไม่ต้องการ ทำไมต้องเป็นdel dirs[:]พิเศษ?
Zach Young

4
โปรดทราบว่าวิธีนี้ใช้ไม่ได้เมื่อใช้topdown=Falseใน os.walk ดูย่อหน้าที่ 4 ในเอกสาร :Modifying dirnames when topdown is False has no effect on the behavior of the walk, because in bottom-up mode the directories in dirnames are generated before dirpath itself is generated.

3
@ZacharyYoung dirs = []และจะไม่ทำงานเพราะพวกเขาเพียงแค่สร้างวัตถุที่ไม่เกี่ยวข้องกันใหม่และกำหนดชื่อdirs = None วัตถุรายการเดิมจะต้องแก้ไขในสถานที่ไม่ได้ชื่อdirs dirs
nosklo

207

อย่าใช้ os.walk

ตัวอย่าง:

import os

root = "C:\\"
for item in os.listdir(root):
    if os.path.isfile(os.path.join(root, item)):
        print item

1
@ 576i: สิ่งนี้ไม่ได้แยกความแตกต่างระหว่างไฟล์และไดเร็กทอรี

4
@Alexandr os.path.isfileและos.path.isdirช่วยให้คุณแยกความแตกต่าง ฉันไม่เข้าใจเพราะos.path.isfileอยู่ในโค้ดตัวอย่างตั้งแต่ '08 และความคิดเห็นของคุณมาจาก '16 นี่เป็นคำตอบที่ดีกว่าอย่างชัดเจนเนื่องจากคุณไม่ได้ตั้งใจที่จะเดินไดเรกทอรี แต่เพื่อแสดงรายการ
Daniel F

@DanielF สิ่งที่ฉันหมายถึงที่นี่คือคุณต้องวนซ้ำรายการทั้งหมดในขณะที่walkให้รายการ dirs และไฟล์แยกกันทันที

อาโอเค. คำตอบของ Alex ดูเหมือนจะดีกว่า (โดยใช้.next()) และใกล้เคียงกับความคิดของคุณมากขึ้น
Daniel F

Python 3.5 มีos.scandirฟังก์ชันที่ช่วยให้การโต้ตอบไฟล์หรือไดเรกทอรี - วัตถุซับซ้อนยิ่งขึ้น ดูคำตอบของฉันด้านล่าง
ascripter

48

ฉันคิดว่าวิธีแก้ปัญหานั้นง่ายมาก

ใช้

break

ในการทำซ้ำสำหรับลูปครั้งแรกเท่านั้นต้องมีวิธีที่สวยงามกว่านี้

for root, dirs, files in os.walk(dir_name):
    for f in files:
        ...
        ...
    break
...

ครั้งแรกที่คุณเรียก os.walk ระบบจะส่งคืนดอกทิวลิปสำหรับไดเร็กทอรีปัจจุบันจากนั้นจึงวนซ้ำเนื้อหาของไดเรกทอรีถัดไป

ใช้สคริปต์เดิมและเพียงแค่เพิ่มการแบ่ง

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        for f in files:
            if os.path.splitext(f)[1] in whitelist:
                outputList.append(os.path.join(root, f))
            else:
                self._email_to_("ignore")
        break
    return outputList

9
นี่ควรเป็นคำตอบที่ได้รับการยอมรับ เพียงเพิ่ม "แบ่ง" หลังลูป "for f ในไฟล์" จะหยุดการวนซ้ำ คุณอาจต้องการตรวจสอบให้แน่ใจว่า topdown = True
Alecz

23

ข้อเสนอแนะที่จะใช้listdirเป็นสิ่งที่ดี คำตอบที่ตรงกับคำถามของคุณในหลาม root, dirs, files = os.walk(dir_name).next()2

ไวยากรณ์ Python 3 ที่เทียบเท่าคือ root, dirs, files = next(os.walk(dir_name))


1
โอ้ฉันได้รับข้อผิดพลาดตลก ๆ จากข้อผิดพลาดนั้น ValueError: มีค่ามากเกินไปที่จะแกะ
Setori

1
ดี! รู้สึกเหมือนถูกแฮ็กแม้ว่า เช่นเดียวกับเมื่อคุณเปิดเครื่องยนต์ แต่ปล่อยให้มันทำการปฏิวัติเพียงครั้งเดียวแล้วดึงกุญแจเพื่อปล่อยให้มันตาย
Daniel F

สะดุดในสิ่งนี้; root, dirs, files = os.walk(dir_name).next()ให้ฉันAttributeError: 'generator' object has no attribute 'next'
Evan

3
@Evan อาจเป็นเพราะมาจากปี 2008 และใช้ไวยากรณ์ Python 2 ใน Python 3 คุณสามารถเขียนได้root, dirs, files = next(os.walk(dir_name))จากนั้นตัวแปรroot, dirs, filesจะสอดคล้องกับตัวแปรของเครื่องกำเนิดไฟฟ้าที่dir_nameระดับเท่านั้น
CervEd

13

คุณสามารถใช้os.listdir()ซึ่งส่งคืนรายการชื่อ (สำหรับทั้งไฟล์และไดเร็กทอรี) ในไดเร็กทอรีที่กำหนด หากคุณต้องการแยกความแตกต่างระหว่างไฟล์และไดเรกทอรีให้เรียกos.stat()ชื่อแต่ละชื่อ


9

หากคุณมีข้อกำหนดที่ซับซ้อนมากกว่าไดเร็กทอรีบนสุด (เช่นละเว้น VCS dirs เป็นต้น) คุณยังสามารถแก้ไขรายการไดเร็กทอรีเพื่อป้องกันไม่ให้ os.walk เรียกซ้ำผ่านได้

กล่าวคือ:

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        dirs[:] = [d for d in dirs if is_good(d)]
        for f in files:
            do_stuff()

หมายเหตุ - โปรดระวังการกลายพันธุ์ของรายการแทนที่จะผูกมัดรายการใหม่ เห็นได้ชัดว่า os.walk ไม่รู้เกี่ยวกับการ rebinding ภายนอก



4

แนวคิดเดียวกันกับlistdirแต่สั้นกว่า:

[f for f in os.listdir(root_dir) if os.path.isfile(os.path.join(root_dir, f))]

3

รู้สึกเหมือนโยนเงิน 2 เพนนีของฉันเข้าไป

baselevel = len(rootdir.split("\\"))
for subdirs, dirs, files in os.walk(rootdir):
    curlevel = len(subdirs.split("\\"))
    if curlevel <= baselevel + 1:
        [do stuff]

2

ใน Python 3 ฉันสามารถทำได้:

import os
dir = "/path/to/files/"

#List all files immediately under this folder:
print ( next( os.walk(dir) )[2] )

#List all folders immediately under this folder:
print ( next( os.walk(dir) )[1] )

สิ่งนี้ใช้ได้กับ Python 2 เช่นกันจะได้รับระดับที่สองได้อย่างไร?

2

ตั้งแต่Python 3.5คุณสามารถใช้os.scandirแทนos.listdirไฟล์. แทนที่จะเป็นสตริงคุณจะได้รับตัววนซ้ำของDirEntryวัตถุในทางกลับกัน จากเอกสาร:

การใช้scandir()แทนlistdir()สามารถเพิ่มประสิทธิภาพของรหัสที่ต้องการข้อมูลประเภทไฟล์หรือแอตทริบิวต์ของไฟล์ได้อย่างมากเนื่องจากDirEntryอ็อบเจ็กต์จะเปิดเผยข้อมูลนี้หากระบบปฏิบัติการจัดเตรียมไว้เมื่อสแกนไดเร็กทอรี DirEntryวิธีการทั้งหมดอาจทำการเรียกระบบ แต่is_dir()และis_file()โดยปกติแล้วจะต้องมีการเรียกระบบสำหรับลิงก์สัญลักษณ์เท่านั้น DirEntry.stat()จำเป็นต้องมีการเรียกระบบบน Unix เสมอ แต่ต้องการเพียงลิงก์เดียวสำหรับลิงก์สัญลักษณ์บน Windows

คุณสามารถเข้าถึงชื่อของวัตถุผ่านDirEntry.nameซึ่งเทียบเท่ากับผลลัพธ์ของos.listdir


1
ไม่เพียง แต่ "สามารถ" คุณใช้คุณควรใช้scandir()ตามที่มันเป็นจำนวนมากlistdir()ได้เร็วกว่า ดูมาตรฐานที่นี่: stackoverflow.com/a/40347279/2441026
user136036

1

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

for path, subdirs, files in os.walk(dir_name):
    for name in files:
        if path == ".": #this will filter the files in the current directory
             #code here

2
สิ่งนี้จะไม่วนลูปผ่าน sub-dir และไฟล์ทั้งหมดโดยไม่จำเป็น?
Pieter

0

นี่คือวิธีที่ฉันแก้ไข

if recursive:
    items = os.walk(target_directory)
else:
    items = [next(os.walk(target_directory))]

...

0

มีการจับเมื่อใช้ listdir os.path.isdir (ตัวระบุ) ต้องเป็นพา ธ สัมบูรณ์ ในการเลือกไดเรกทอรีย่อยให้ทำดังนี้

for dirname in os.listdir(rootdir):
  if os.path.isdir(os.path.join(rootdir, dirname)):
     print("I got a subdirectory: %s" % dirname)

อีกทางเลือกหนึ่งคือการเปลี่ยนเป็นไดเร็กทอรีเพื่อทำการทดสอบโดยไม่มี os.path.join ()



0

สร้างรายการที่ไม่รวมใช้ fnmatch เพื่อข้ามโครงสร้างไดเร็กทอรีและทำกระบวนการ

excludes= ['a\*\b', 'c\d\e']
for root, directories, files in os.walk('Start_Folder'):
    if not any(fnmatch.fnmatch(nf_root, pattern) for pattern in excludes):
        for root, directories, files in os.walk(nf_root):
            ....
            do the process
            ....

เช่นเดียวกับ "รวม":

if **any**(fnmatch.fnmatch(nf_root, pattern) for pattern in **includes**):

0

ทำไมไม่ใช้ a rangeและos.walkรวมกับzip? ไม่ใช่ทางออกที่ดีที่สุด แต่ก็ใช้ได้ผลเช่นกัน

ตัวอย่างเช่นนี้:

# your part before
for count, (root, dirs, files) in zip(range(0, 1), os.walk(dir_name)):
    # logic stuff
# your later part

ใช้ได้กับฉันใน python 3

นอกจากนี้: A breakนั้นง่ายกว่าด้วย btw (ดูคำตอบจาก @Pieter)


0

การเปลี่ยนแปลงเล็กน้อยกับคำตอบของ Alex แต่ใช้__next__():

print(next(os.walk('d:/'))[2]) หรือ print(os.walk('d:/').__next__()[2])

กับ[2]การเป็นfileในroot, dirs, fileการกล่าวถึงในคำตอบอื่น ๆ


0

การเปลี่ยนแปลงโฟลเดอร์รูทสำหรับทุกไดเร็กทอรี os.walk พบ ฉันแก้ปัญหานั้นตรวจสอบว่าไดเรกทอรี root ==

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        if root == dir_name: #This only meet parent folder
            for f in files:
                if os.path.splitext(f)[1] in whitelist:
                    outputList.append(os.path.join(root, f))
                else:
                    self._email_to_("ignore")
    return outputList

0
import os

def listFiles(self, dir_name):
    names = []
    for root, directory, files in os.walk(dir_name):
        if root == dir_name:
            for name in files:
                names.append(name)
    return names

1
สวัสดีรวยยินดีต้อนรับสู่ Stack Overflow! ขอขอบคุณสำหรับข้อมูลโค้ดนี้ซึ่งอาจให้ความช่วยเหลือระยะสั้นที่ จำกัด คำอธิบายที่เหมาะสมจะช่วยเพิ่มมูลค่าในระยะยาวได้อย่างมากโดยแสดงให้เห็นว่าเหตุใดจึงเป็นวิธีแก้ปัญหาที่ดีและจะทำให้มีประโยชน์มากขึ้นสำหรับผู้อ่านในอนาคตที่มีคำถามอื่น ๆ ที่คล้ายคลึงกัน โปรดแก้ไขคำตอบของคุณเพื่อเพิ่มคำอธิบายรวมถึงสมมติฐานที่คุณตั้งขึ้น
kenny_k
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.