os.walk tanpa membuka direktori di bawah ini


103

Bagaimana cara membatasi os.walkuntuk hanya mengembalikan file di direktori yang saya sediakan?

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
Kasus lain di mana banyak kemungkinan pendekatan dan semua peringatan yang menyertainya menunjukkan bahwa fungsionalitas ini harus ditambahkan ke pustaka standar Python.
semut

files_with_full_path = [f.path for f in os.scandir(dir) if f.is_file()]. Jika Anda hanya perlu menggunakan nama file, f.namebukan f.path. Ini adalah solusi tercepat dan jauh lebih cepat daripada yang mana pun walkatau listdir, lihat stackoverflow.com/a/40347279/2441026 .
pengguna136036

Jawaban:


105

Gunakan walklevelfungsinya.

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[:]

Ini berfungsi seperti os.walk, tetapi Anda dapat meneruskannya ke levelparameter yang menunjukkan seberapa dalam rekursi akan berjalan.


3
Apakah fungsi ini benar-benar "berjalan" melalui seluruh struktur dan kemudian menghapus entri di bawah titik tertentu? Atau sesuatu yang lebih pintar sedang terjadi? Saya bahkan tidak yakin bagaimana memeriksa ini dengan kode. --python pemula
mathtick

1
@mathtick: ketika beberapa direktori pada atau di bawah tingkat yang diinginkan ditemukan, semua subdirektori dihapus dari daftar subdir untuk mencari berikutnya. Jadi mereka tidak akan "berjalan".
nosklo

2
Saya baru saja memberi ini +1 karena saya kesulitan dengan cara "menghapus" dirs. Saya telah mencoba dirs = []dan dirs = Nonetetapi itu tidak berhasil. map(dirs.remove, dirs)bekerja, tetapi dengan beberapa pesan '[Tidak Ada]' yang tidak diinginkan dicetak. Jadi, mengapa del dirs[:]secara khusus?
Zach Young

4
Perhatikan bahwa ini tidak berfungsi saat menggunakan topdown=Falsedi os.walk. Lihat paragraf ke-4 dalam dokumen :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.
dthor

3
@ZacharyYoung dirs = []dan dirs = Nonetidak akan berfungsi karena mereka hanya membuat objek baru yang tidak terkait dan menetapkan namanya dirs. Objek daftar asli perlu dimodifikasi di tempat, bukan namanya dirs.
nosklo

206

Jangan gunakan os.walk.

Contoh:

import os

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

1
@ 576i: ini tidak membedakan antara berkas dan direktori

4
@Alexandr os.path.isfiledan os.path.isdirmemungkinkan Anda membedakan. Saya tidak mengerti, karena os.path.isfileada dalam kode contoh sejak '08 dan komentar Anda berasal dari '16. Ini jelas merupakan jawaban yang lebih baik, karena Anda tidak bermaksud untuk menjalankan direktori, tetapi untuk mendaftarnya.
Daniel F

@DanielF, yang saya maksud di sini adalah bahwa Anda perlu mengulang semua item, sementara walksegera memberi Anda daftar terpisah dari dirs dan file.

Ah, baiklah. Sebenarnya jawaban Alex tampaknya lebih baik (menggunakan .next()) dan lebih dekat dengan ide Anda.
Daniel F

Python 3.5 memiliki os.scandirfungsi yang memungkinkan interaksi file-atau-direktori-objek yang lebih canggih. Lihat jawaban saya di bawah ini
ascripter

48

Menurut saya solusinya sebenarnya sangat sederhana.

menggunakan

break

untuk hanya melakukan iterasi pertama pada loop for, harus ada cara yang lebih elegan.

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

Pertama kali Anda memanggil os.walk, ia mengembalikan tulip untuk direktori saat ini, kemudian pada perulangan berikutnya isi direktori berikutnya.

Ambil skrip asli dan tambahkan jeda .

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
Ini seharusnya jawaban yang diterima. Cukup menambahkan "break" setelah loop "untuk f dalam file" menghentikan rekursif. Anda mungkin juga ingin memastikan bahwa topdown = True.
Alecz

23

Saran untuk digunakan listdiritu bagus. Jawaban langsung untuk pertanyaan Anda dengan Python 2 adalah root, dirs, files = os.walk(dir_name).next().

Sintaks setara Python 3 adalah root, dirs, files = next(os.walk(dir_name))


1
Oh, saya mendapatkan semua jenis kesalahan lucu dari yang satu itu. ValueError: terlalu banyak nilai untuk
dibuka

1
Bagus! Terasa seperti hack. Seperti ketika Anda menyalakan mesin tetapi hanya membiarkannya melakukan satu putaran lalu menarik kunci untuk membiarkannya mati.
Daniel F

Tersandung ini; root, dirs, files = os.walk(dir_name).next()memberi sayaAttributeError: 'generator' object has no attribute 'next'
Evan

3
@ Evan, mungkin karena ini dari tahun 2008 dan menggunakan sintaks Python 2. Di Python 3 Anda dapat menulis root, dirs, files = next(os.walk(dir_name))dan kemudian variabel root, dirs, fileshanya akan sesuai dengan variabel generator di dir_namelevel.
CervEd

13

Anda bisa menggunakan os.listdir()yang mengembalikan daftar nama (untuk file dan direktori) di direktori tertentu. Jika Anda perlu membedakan antara file dan direktori, panggil os.stat()setiap nama.


9

Jika Anda memiliki persyaratan yang lebih kompleks daripada hanya direktori teratas (misalnya mengabaikan direktori VCS, dll.), Anda juga dapat mengubah daftar direktori untuk mencegah os.walk berulang kali melewatinya.

yaitu:

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

Catatan - hati-hati untuk mengubah daftar, bukan hanya mengulangnya. Jelas os.walk tidak tahu tentang rebinding eksternal.


6
for path, dirs, files in os.walk('.'):
    print path, dirs, files
    del dirs[:] # go only one level deep

4

Ide yang sama dengan listdir, tetapi lebih pendek:

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

3

Merasa ingin membuang 2 pence saya.

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

2

Dengan Python 3, saya bisa melakukan ini:

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

Ini juga berlaku untuk Python 2. Bagaimana cara mendapatkan level kedua?

2

Sejak Python 3.5 Anda dapat menggunakan os.scandirsebagai pengganti os.listdir. Alih-alih string, Anda mendapatkan iterator DirEntryobjek sebagai gantinya. Dari dokumen:

Menggunakan scandir()alih-alih listdir()dapat secara signifikan meningkatkan kinerja kode yang juga memerlukan jenis file atau informasi atribut file, karena DirEntryobjek mengekspos informasi ini jika sistem operasi menyediakannya saat memindai direktori. Semua DirEntrymetode dapat melakukan panggilan sistem, tetapi is_dir()dan is_file()biasanya hanya memerlukan panggilan sistem untuk tautan simbolik; DirEntry.stat()selalu membutuhkan panggilan sistem di Unix tetapi hanya memerlukan satu panggilan untuk tautan simbolik di Windows.

Anda dapat mengakses nama objek DirEntry.nameyang kemudian setara dengan output darios.listdir


1
Tidak hanya "dapat" Anda menggunakan, Anda harus menggunakan scandir(), karena jauh lebih cepat daripada listdir(). Lihat tolok ukur di sini: stackoverflow.com/a/40347279/2441026 .
pengguna136036

1

Anda juga bisa melakukan hal berikut:

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
Tidakkah ini akan mengulang semua sub-dir dan file secara tidak perlu?
Pieter

0

Beginilah cara saya menyelesaikannya

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

...

0

Ada kendala saat menggunakan listdir. Os.path.isdir (pengenal) harus berupa jalur absolut. Untuk memilih subdirektori yang Anda lakukan:

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

Alternatifnya adalah mengubah ke direktori untuk melakukan pengujian tanpa os.path.join ().


0

Anda dapat menggunakan potongan ini

for root, dirs, files in os.walk(directory):
    if level > 0:
        # do some stuff
    else:
        break
    level-=1

0

buat daftar pengecualian, gunakan fnmatch untuk melewati struktur direktori dan lakukan prosesnya

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

sama seperti untuk 'termasuk':

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

0

Mengapa tidak menggunakan a rangedan os.walkdikombinasikan dengan zip? Bukan solusi terbaik, tapi akan berhasil juga.

Contohnya seperti ini:

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

Bekerja untuk saya di python 3.

Juga: A breaklebih sederhana juga btw. (Lihat jawaban dari @Pieter)


0

Sedikit perubahan pada jawaban Alex, tetapi menggunakan __next__():

print(next(os.walk('d:/'))[2]) atau print(os.walk('d:/').__next__()[2])

dengan [2]menjadi filedi root, dirs, filedisebutkan dalam jawaban lainnya


0

folder root berubah untuk setiap direktori yang ditemukan os.walk. Saya memecahkan memeriksa apakah root == direktori

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
Hai Rich, selamat datang di Stack Overflow! Terima kasih atas cuplikan kode ini, yang mungkin memberikan beberapa bantuan jangka pendek terbatas. Penjelasan yang tepat akan sangat meningkatkan nilai jangka panjangnya dengan menunjukkan mengapa ini adalah solusi yang baik untuk masalah tersebut, dan akan membuatnya lebih berguna bagi pembaca di masa mendatang dengan pertanyaan serupa lainnya. Harap edit jawaban Anda untuk menambahkan penjelasan, termasuk asumsi yang Anda buat.
kenny_k
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.