os.walk mà không cần đào sâu vào các thư mục bên dưới


103

Làm cách nào để giới hạn os.walkchỉ trả lại các tệp trong thư mục mà tôi cung cấp?

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
Một trường hợp khác trong đó có vô số cách tiếp cận có thể có và tất cả các cảnh báo đi kèm với chúng cho thấy rằng chức năng này nên được thêm vào thư viện chuẩn Python.
antred

files_with_full_path = [f.path for f in os.scandir(dir) if f.is_file()]. Trong trường hợp bạn chỉ cần sử dụng tên tệp f.namethay vì f.path. Đây là giải pháp nhanh nhất và nhanh hơn nhiều so với bất kỳ giải pháp nào walkhoặc listdirxem stackoverflow.com/a/40347279/2441026 .
user136036

Câu trả lời:


105

Sử dụng walklevelchức năng.

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

Nó hoạt động giống như vậy os.walk, nhưng bạn có thể chuyển cho nó một leveltham số cho biết mức độ sâu của đệ quy.


3
Chức năng này có thực sự "đi" qua toàn bộ cấu trúc và sau đó xóa các mục bên dưới một điểm nhất định không? Hay là một cái gì đó thông minh hơn đang diễn ra? Tôi thậm chí không chắc chắn làm thế nào để kiểm tra điều này bằng mã. --python mới bắt đầu
mathtick

1
@mathtick: khi tìm thấy một số thư mục trên hoặc dưới cấp mong muốn, tất cả các thứ tự con của nó sẽ bị xóa khỏi danh sách các thứ tự con để tìm kiếm tiếp theo. Vì vậy, họ sẽ không bị "đi bộ".
nosklo

2
Tôi chỉ +1 điều này vì tôi đang đấu tranh với cách "xóa" dirs. Tôi đã cố gắng dirs = []dirs = Nonenhưng những người không làm việc. map(dirs.remove, dirs)đã hoạt động, nhưng với một số thông báo '[Không]' không mong muốn được in. Vậy, del dirs[:]cụ thể là tại sao ?
Zach Young

4
Lưu ý rằng điều này không hoạt động khi sử dụng topdown=Falsetrong os.walk. Xem đoạn thứ 4 trong tài liệu :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 = []dirs = Nonesẽ không hoạt động vì họ chỉ tạo một đối tượng mới không liên quan và gán cho tên dirs. Đối tượng danh sách ban đầu cần được sửa đổi tại chỗ, không phải tên dirs.
nosklo

206

Không sử dụng os.walk.

Thí dụ:

import os

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

1
@ 576i: đây không phân biệt giữa các tập tin và thư mục

4
@Alexandr os.path.isfileos.path.isdircho phép bạn phân biệt. Tôi không hiểu, vì os.path.isfilenó nằm trong mã mẫu từ '08 và nhận xét của bạn là từ '16. Đây rõ ràng là câu trả lời tốt hơn, vì bạn không có ý định xem một danh mục mà là liệt kê nó.
Daniel F

@DanielF, ý tôi muốn nói ở đây là bạn cần lặp lại tất cả các mục, đồng thời walkcung cấp cho bạn ngay lập tức danh sách dirs và tệp riêng biệt.

À, được rồi. Trên thực tế câu trả lời của Alex có vẻ tốt hơn (đang sử dụng .next()) và nó gần với ý tưởng của bạn hơn nhiều.
Daniel F

Python 3.5 có một os.scandirchức năng cho phép tương tác tệp hoặc thư mục-đối tượng phức tạp hơn. Xem câu trả lời của tôi bên dưới
ascripter

48

Tôi nghĩ rằng giải pháp thực sự rất đơn giản.

sử dụng

break

để chỉ thực hiện lần lặp đầu tiên của vòng lặp for, phải có một cách thanh lịch hơn.

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

Lần đầu tiên bạn gọi os.walk, nó trả về hoa tulip cho thư mục hiện tại, sau đó trong vòng lặp tiếp theo nội dung của thư mục tiếp theo.

Lấy kịch bản gốc và chỉ cần thêm một đoạn nghỉ .

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
Đây lẽ ra phải là câu trả lời được chấp nhận. Chỉ cần thêm một "break" sau vòng lặp "for f in files" sẽ dừng việc đệ quy. Bạn cũng có thể muốn đảm bảo rằng topdown = True.
Alecz,

23

Đề xuất để sử dụng listdirlà một trong những tốt. Câu trả lời trực tiếp cho câu hỏi của bạn trong Python 2 là root, dirs, files = os.walk(dir_name).next().

Cú pháp Python 3 tương đương là root, dirs, files = next(os.walk(dir_name))


1
Ồ, tôi đã nhận được tất cả các loại lỗi buồn cười từ cái đó. ValueError: quá nhiều giá trị để giải nén
Setori

1
Đẹp! Tuy nhiên, cảm thấy giống như một vụ hack. Giống như khi bạn nổ máy nhưng chỉ để nó làm một vòng rồi kéo chìa khóa để nó chết.
Daniel F

Tình cờ gặp phải điều này; root, dirs, files = os.walk(dir_name).next()cho tôiAttributeError: 'generator' object has no attribute 'next'
Evan

3
@Evan, có lẽ vì đây là từ năm 2008 và sử dụng cú pháp Python 2. Trong Python 3, bạn có thể viết root, dirs, files = next(os.walk(dir_name))và sau đó các biến root, dirs, filessẽ chỉ tương ứng với các biến của trình tạo ở dir_namecấp độ.
CervEd

13

Bạn có thể sử dụng os.listdir()nó trả về danh sách các tên (cho cả tệp và thư mục) trong một thư mục nhất định. Nếu bạn cần phân biệt giữa tệp và thư mục, hãy gọi os.stat()từng tên.


9

Nếu bạn có nhiều yêu cầu phức tạp hơn chỉ là thư mục trên cùng (ví dụ: bỏ qua các dirs VCS, v.v.), bạn cũng có thể sửa đổi danh sách các thư mục để ngăn os.walk đệ quy qua chúng.

I E:

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

Lưu ý - hãy cẩn thận thay đổi danh sách, thay vì chỉ gắn lại nó. Rõ ràng là os.walk không biết về sự ràng buộc bên ngoài.


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

4

Ý tưởng tương tự với listdir, nhưng ngắn hơn:

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

3

Cảm giác như ném 2 pence của tôi vào.

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

2

Trong Python 3, tôi đã có thể làm điều này:

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

Điều này cũng hoạt động cho Python 2. Làm thế nào để có được cấp độ thứ hai?

2

Kể từ Python 3.5, bạn có thể sử dụng os.scandirthay thế os.listdir. Thay vì các chuỗi, bạn nhận được một trình lặp lại các DirEntryđối tượng. Từ các tài liệu:

Việc sử dụng scandir()thay vì listdir()có thể làm tăng đáng kể hiệu suất của mã cũng cần loại tệp hoặc thông tin thuộc tính tệp, vì DirEntrycác đối tượng tiết lộ thông tin này nếu hệ điều hành cung cấp thông tin đó khi quét thư mục. Tất cả các DirEntryphương thức có thể thực hiện một lệnh gọi hệ thống, nhưng is_dir()is_file()thường chỉ yêu cầu một lệnh gọi hệ thống cho các liên kết tượng trưng; DirEntry.stat()luôn yêu cầu lệnh gọi hệ thống trên Unix nhưng chỉ yêu cầu một lệnh gọi cho các liên kết tượng trưng trên Windows.

Bạn có thể truy cập vào tên của đối tượng DirEntry.namemà qua đó tương đương với đầu ra củaos.listdir


1
Không chỉ "có thể" bạn sử dụng, bạn nên sử dụng scandir(), vì nó nhanh hơn rất nhiềulistdir() . Xem điểm chuẩn tại đây: stackoverflow.com/a/40347279/2441026 .
user136036

1

Bạn cũng có thể làm như sau:

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
Điều này sẽ lặp lại qua tất cả các tiểu dir và các tệp một cách không cần thiết phải không?
Pieter

0

Đây là cách tôi đã giải quyết nó

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

...

0

Có một vấn đề khi sử dụng listdir. Os.path.isdir (định danh) phải là một đường dẫn tuyệt đối. Để chọn các thư mục con, bạn thực hiện:

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

Cách thay thế là thay đổi thư mục để thực hiện kiểm tra mà không có os.path.join ().


0

Bạn có thể sử dụng đoạn mã này

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

0

tạo danh sách các loại trừ, sử dụng fnmatch để bỏ qua cấu trúc thư mục và thực hiện quy trình

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

giống như đối với 'bao gồm':

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

0

Tại sao không chỉ đơn giản sử dụng a rangeos.walkkết hợp với zip? Không phải là giải pháp tốt nhất, nhưng cũng sẽ hoạt động.

Ví dụ như thế này:

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

Làm việc cho tôi trên python 3.

Ngoài ra: A breakcũng đơn giản hơn btw. (Xem câu trả lời từ @Pieter)


0

Một chút thay đổi đối với câu trả lời của Alex, nhưng sử dụng __next__():

print(next(os.walk('d:/'))[2]) hoặc là print(os.walk('d:/').__next__()[2])

với [2]filetrong root, dirs, fileđề cập trong câu trả lời khác


0

thư mục gốc thay đổi cho mọi thư mục mà os.walk tìm thấy. Tôi giải quyết rằng kiểm tra xem thư mục 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
Xin chào Rich, chào mừng bạn đến với Stack Overflow! Cảm ơn bạn về đoạn mã này, đoạn mã có thể cung cấp một số trợ giúp ngắn hạn có giới hạn. Một lời giải thích phù hợp sẽ cải thiện đáng kể giá trị lâu dài của nó bằng cách chỉ ra lý do tại sao đây là một giải pháp tốt cho vấn đề và sẽ làm cho nó hữu ích hơn cho những người đọc trong tương lai với những câu hỏi tương tự. Vui lòng chỉnh sửa câu trả lời của bạn để thêm một số giải thích, bao gồm cả những giả định bạn đã đưa ra.
kenny_k
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.