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

nosklo.dll
sumber
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
Yuval Adam
sumber
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
Pieter
sumber
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))

Alex Coventry
sumber
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.

Greg Hewgill
sumber
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.

Brian
sumber
6
for path, dirs, files in os.walk('.'):
    print path, dirs, files
    del dirs[:] # go only one level deep
masterxilo
sumber
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))]
Oleg Gryb
sumber
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]
Matt R
sumber
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] )
Jay Sheth
sumber
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

ascripter
sumber
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
Diana G
sumber
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))]

...
Mulia
sumber
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 ().

Kemin Zhou
sumber
0

Anda dapat menggunakan potongan ini

for root, dirs, files in os.walk(directory):
    if level > 0:
        # do some stuff
    else:
        break
    level-=1
RousseauAlexandre
sumber
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**):
Hamsavardhini
sumber
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)

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

Oleg
sumber
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
Pedro J. Sola
sumber
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
Kaya
sumber
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