Tidak termasuk direktori di os.walk

148

Saya sedang menulis skrip yang turun ke pohon direktori (menggunakan os.walk ()) dan kemudian mengunjungi setiap file yang cocok dengan ekstensi file tertentu. Namun, karena beberapa pohon direktori tempat alat saya akan digunakan juga mengandung sub direktori yang pada gilirannya mengandung BANYAK hal yang tidak berguna (untuk tujuan skrip ini), saya pikir saya akan menambahkan opsi bagi pengguna untuk menentukan daftar direktori untuk dikecualikan dari traversal.

Ini cukup mudah dengan os.walk (). Setelah semua, terserah saya untuk memutuskan apakah saya benar-benar ingin mengunjungi masing-masing file / dir yang dihasilkan oleh os.walk () atau hanya melewatkannya. Masalahnya adalah jika saya memiliki, misalnya, pohon direktori seperti ini:

root--
     |
     --- dirA
     |
     --- dirB
     |
     --- uselessStuff --
                       |
                       --- moreJunk
                       |
                       --- yetMoreJunk

dan saya ingin mengecualikanStuff yang tidak berguna dan semua anak-anaknya, os.walk () masih akan turun ke semua (berpotensi ribuan) sub direktori dari Stuff yang tidak berguna , yang, tentu saja, banyak memperlambat segalanya. Dalam dunia yang ideal, saya bisa mengatakan os.walk () untuk tidak repot-repot menghasilkan anak lagi dari Stuff yang tidak berguna , tetapi setahu saya tidak ada cara untuk melakukan itu (ada?).

Adakah yang punya ide? Mungkin ada perpustakaan pihak ketiga yang menyediakan sesuatu seperti itu?

antred
sumber

Jawaban:

243

Memodifikasi dirs di tempat akan memangkas file (dan selanjutnya) direktori dan dikunjungi oleh os.walk:

# exclude = set([...])
for root, dirs, files in os.walk(top, topdown=True):
    dirs[:] = [d for d in dirs if d not in exclude]

Dari bantuan (os.walk):

Ketika topdown benar, penelepon dapat mengubah daftar dirnames di tempat (misalnya, melalui tugas del atau slice), dan walk hanya akan muncul kembali ke dalam subdirektori yang namanya tetap dalam dirnames; ini dapat digunakan untuk memangkas pencarian ...

unutbu
sumber
31
Mengapa dirs[:] =?
ben
56
@ben: dirs[:] = valuememodifikasi dirs di tempat . Ini mengubah isi daftar dirstanpa mengubah wadah. Seperti yang help(os.walk)disebutkan, ini diperlukan jika Anda ingin memengaruhi cara os.walkmelintasi subdirektori. ( dirs = valuehanya menetapkan ulang (atau "mengikat") variabel dirske daftar baru, tanpa mengubah yang asli dirs.)
unutbu
6
Anda juga dapat menggunakan filter():dirs[:] = list(filter(lambda x: not x in exclude, dirs))
NuclearPeon
2
@ p014k: Anda dapat menulis fungsi generator Anda sendiri yang memanggil os.walkdan menghasilkan root, dirs, filessetelah mengecualikan .git(atau apa pun yang Anda inginkan) dari dirs.
unutbu
3
@unutbu Hanya memberi tahu Anda bahwa dalam satu kasus, pengoptimalan ini telah mengurangi waktu traversal dari lebih dari 100 detik menjadi sekitar 2 detik. Itulah yang saya sebut optimasi yang bermanfaat. : D
antred
7

... bentuk alternatif jawaban luar biasa @ unutbu yang membaca sedikit lebih langsung, mengingat tujuannya adalah untuk mengecualikan direktori, dengan biaya O (n ** 2) vs O (n) waktu.

(Membuat salinan daftar dir dengan list(dirs)diperlukan untuk eksekusi yang benar)

# exclude = set([...])
for root, dirs, files in os.walk(top, topdown=True):
    [dirs.remove(d) for d in list(dirs) if d in exclude]
Dmitri
sumber
5
Jika Anda ingin lebih langsung dengan mengorbankan sejumlah memori, Anda sebaiknya menulis dirs[:] = set(dirs) - exclude. Setidaknya masih \ $ O (n) \ $ dan Anda tidak membangun pemahaman hanya untuk efek sampingnya ...
301_Moved_Permanently
3
Ini bukan benar-benar buruk tetapi bukan Python idiomatis baik menurut saya.
Torsten Bronger
for d in list(dirs)agak aneh. dirssudah menjadi daftar. Dan apa yang Anda miliki sebenarnya bukan daftar pemahaman. dirs.remove(d)tidak mengembalikan apa pun, sehingga Anda berakhir dengan daftar penuh Nones. Saya setuju dengan @Torsten.
seanahern