a

Câu hỏi Làm thế nào tôi có thể tạo một thư mục lồng nhau một cách an toàn bằng Python?


Cách thanh lịch nhất để kiểm tra xem thư mục một tập tin sẽ được ghi là có tồn tại không, và nếu không, hãy tạo thư mục bằng Python? Đây là những gì tôi đã thử:

import os

file_path = "/my/directory/filename.txt"
directory = os.path.dirname(file_path)

try:
    os.stat(directory)
except:
    os.mkdir(directory)       

f = file(filename)

Bằng cách nào đó, tôi đã bỏ lỡ os.path.exists (cảm ơn kanja, Blair, và Douglas). Đây là những gì tôi có bây giờ:

def ensure_dir(file_path):
    directory = os.path.dirname(file_path)
    if not os.path.exists(directory):
        os.makedirs(directory)

Có một lá cờ cho "mở", điều đó làm cho điều này xảy ra tự động?


2989
2017-11-07 18:56


gốc


Nói chung, bạn có thể cần phải giải thích cho trường hợp không có thư mục nào trong tên tệp. Trên máy dirname của tôi ('foo.txt') cho '', không tồn tại và gây ra makedirs () thất bại. - Brian Hawkins
Trong python 2,7 os.path.mkdir không tồn tại. nó là os.mkdir. - drevicko
nếu đường dẫn tồn tại không chỉ kiểm tra xem đó là thư mục và không phải là tệp thông thường hay đối tượng khác (nhiều câu trả lời kiểm tra điều này) thì cũng cần kiểm tra xem nó có thể ghi được không (tôi không tìm thấy câu trả lời đã kiểm tra) - miracle173
Trong trường hợp bạn đến đây để tạo thư mục cha của chuỗi đường dẫn tệp p, đây là đoạn mã của tôi: os.makedirs(p[:p.rindex(os.path.sep)], exist_ok=True) - Thamme Gowda
thảo luận meta các câu trả lời trong câu hỏi này - gnat


Các câu trả lời:


Tôi thấy hai câu trả lời với những phẩm chất tốt, mỗi câu trả lời đều có một lỗ hổng nhỏ, vì vậy tôi sẽ đưa tôi vào đó:

Thử os.path.existsvà cân nhắc os.makedirs để tạo ra.

import os
if not os.path.exists(directory):
    os.makedirs(directory)

Như đã lưu ý trong các nhận xét và ở nơi khác, có một điều kiện chủng tộc - nếu thư mục được tạo giữa os.path.exists và os.makedirs cuộc gọi, os.makedirs sẽ thất bại với một OSError. Thật không may, bắt được OSError và tiếp tục không phải là điều dễ hiểu, vì nó sẽ bỏ qua một sự thất bại trong việc tạo thư mục do các yếu tố khác, chẳng hạn như không đủ quyền, đĩa đầy đủ, v.v.

Một lựa chọn sẽ là bẫy OSError và kiểm tra mã lỗi được nhúng (xem Có cách tiếp cận đa nền tảng để nhận thông tin từ OSError của Python không):

import os, errno

try:
    os.makedirs(directory)
except OSError as e:
    if e.errno != errno.EEXIST:
        raise

Ngoài ra, có thể có một giây os.path.exists, nhưng giả sử người khác tạo thư mục sau lần kiểm tra đầu tiên, sau đó xóa nó trước lần kiểm tra thứ hai - chúng tôi vẫn có thể bị lừa.

Tùy thuộc vào ứng dụng, nguy cơ của các hoạt động đồng thời có thể nhiều hơn hoặc ít hơn mức độ nguy hiểm do các yếu tố khác như quyền truy cập tệp đặt ra. Các nhà phát triển sẽ phải biết thêm về các ứng dụng cụ thể đang được phát triển và môi trường dự kiến ​​của nó trước khi chọn một thực hiện.


3691
2017-11-07 19:06



đã đồng ý giải pháp thử / ngoại trừ tốt hơn - Corey Goldberg
Hãy nhớ rằng os.path.exists () không phải là miễn phí. Nếu trường hợp thông thường là thư mục sẽ ở đó, thì trường hợp không nên xử lý như một ngoại lệ. Nói cách khác, hãy thử mở và ghi vào tệp của bạn, bắt ngoại lệ OSError và dựa trên errno, thực hiện lệnh makedir () của bạn và thử lại hoặc tăng lại. Điều này tạo ra sự trùng lặp của mã trừ khi bạn quấn các văn bản trong một phương pháp địa phương. - Andrew
os.path.exists cũng trả về True cho một tập tin. Tôi đã đăng câu trả lời để giải quyết vấn đề này. - A-B-B
os.mkdirs() có thể tạo các thư mục ngoài ý muốn nếu dấu phân tách đường dẫn vô tình bị bỏ sót, thư mục hiện tại không như mong đợi, phần tử đường dẫn chứa dấu tách đường dẫn. Nếu bạn dùng os.mkdir() những lỗi này sẽ gây ra một ngoại lệ, cảnh báo bạn về sự tồn tại của chúng. - drevicko
vì vậy câu trả lời thực tế nên stackoverflow.com/questions/273192/… - user3885927


Python 3.5+:

import pathlib
pathlib.Path('/my/directory').mkdir(parents=True, exist_ok=True) 

pathlib.Path.mkdir như được sử dụng ở trên đệ quy tạo thư mục và không tăng ngoại lệ nếu thư mục đã tồn tại. Nếu bạn không cần hoặc muốn cha mẹ được tạo ra, hãy bỏ qua parents tranh luận.

Python 3.2+:

Sử dụng pathlib:

Nếu bạn có thể, cài đặt hiện tại pathlib backport có tên pathlib2. Không cài đặt backport unmaintained cũ hơn pathlib. Tiếp theo, hãy tham khảo phần Python 3.5+ ở trên và sử dụng nó giống nhau.

Nếu sử dụng Python 3.4, mặc dù nó đi kèm với pathlib, nó thiếu hữu ích exist_ok Tùy chọn. Backport được thiết kế để cung cấp một triển khai mới hơn và cao cấp hơn mkdir bao gồm tùy chọn bị thiếu này.

Sử dụng os:

import os
os.makedirs(path, exist_ok=True)

os.makedirs như được sử dụng ở trên đệ quy tạo thư mục và không tăng ngoại lệ nếu thư mục đã tồn tại. Nó có tùy chọn exist_ok đối số chỉ khi sử dụng Python 3.2+, với giá trị mặc định là False. Đối số này không tồn tại trong Python 2.x lên tới 2.7. Như vậy, không cần xử lý ngoại lệ thủ công như với Python 2.7.

Python 2,7+:

Sử dụng pathlib:

Nếu bạn có thể, cài đặt hiện tại pathlib backport có tên pathlib2. Không cài đặt backport unmaintained cũ hơn pathlib. Tiếp theo, hãy tham khảo phần Python 3.5+ ở trên và sử dụng nó giống nhau.

Sử dụng os:

import os
try: 
    os.makedirs(path)
except OSError:
    if not os.path.isdir(path):
        raise

Trong khi một giải pháp ngây thơ đầu tiên có thể sử dụng os.path.isdir theo dõi bởi os.makedirs, giải pháp trên đảo ngược thứ tự của hai thao tác. Trong khi làm như vậy, nó ngăn chặn một điều kiện chủng tộc phổ biến phải làm với một nỗ lực trùng lặp trong việc tạo ra các thư mục, và cũng disambiguates tập tin từ thư mục.

Lưu ý rằng chụp ngoại lệ và sử dụng errno hữu ích hạn chế bởi vì OSError: [Errno 17] File exists, I E. errno.EEXIST, được nâng lên cho cả tệp và thư mục. Nó là đáng tin cậy hơn chỉ đơn giản là để kiểm tra xem thư mục tồn tại.

Thay thế:

mkpath tạo thư mục lồng nhau và không làm gì nếu thư mục đã tồn tại. Điều này làm việc trong cả Python 2 và 3.

import distutils.dir_util
distutils.dir_util.mkpath(path)

Mỗi Lỗi 10948, một hạn chế nghiêm trọng của phương án này là nó hoạt động chỉ một lần cho mỗi quá trình python cho một đường dẫn nhất định. Nói cách khác, nếu bạn sử dụng nó để tạo một thư mục, sau đó xóa thư mục từ bên trong hoặc bên ngoài Python, sau đó sử dụng mkpath một lần nữa để tạo lại cùng một thư mục, mkpath chỉ đơn giản là sẽ sử dụng âm thầm thông tin được lưu trữ không hợp lệ của nó trước đây đã tạo thư mục và sẽ không thực sự tạo lại thư mục. Ngược lại, os.makedirs không dựa vào bất kỳ bộ nhớ cache nào như vậy. Giới hạn này có thể được chấp nhận đối với một số ứng dụng.


Liên quan đến thư mục chế độ, vui lòng tham khảo tài liệu nếu bạn quan tâm.


815
2018-01-16 17:31



Câu trả lời này bao gồm khá nhiều trường hợp đặc biệt theo như tôi có thể nói. Tôi có kế hoạch gói này trong một "nếu không os.path.isdir ()" mặc dù kể từ khi tôi mong đợi các thư mục để tồn tại hầu như mọi thời gian và tôi có thể tránh ngoại lệ theo cách đó. - Charles L.
@CharlesL. Một ngoại lệ có lẽ là rẻ hơn so với IO đĩa của kiểm tra, nếu lý do của bạn là hiệu suất. - jpmc26
@ jpmc26 nhưng makedirs không bổ sung stat, umask, lstat khi chỉ kiểm tra để ném OSError. - kwarunek
Đây là câu trả lời sai, vì nó giới thiệu một cuộc đua FS tiềm năng. Xem câu trả lời từ Aaron Hall. - sleepycal
như @sleepycal đã nói, điều này bị một tình trạng giống như câu trả lời được chấp nhận. Nếu giữa việc nâng lỗi và kiểm tra os.path.isdir người khác xóa thư mục, bạn sẽ tăng sai, lỗi thời và gây nhầm lẫn lỗi mà thư mục tồn tại. - farmir


Sử dụng thử ngoại trừ và mã lỗi đúng từ mô-đun errno được loại bỏ điều kiện chủng tộc và là nền tảng chéo:

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

Nói cách khác, chúng tôi cố gắng tạo các thư mục, nhưng nếu chúng đã tồn tại, chúng tôi bỏ qua lỗi. Mặt khác, bất kỳ lỗi nào khác được báo cáo. Ví dụ: nếu bạn tạo dir 'a' trước và xóa tất cả quyền từ nó, bạn sẽ nhận được OSError lớn lên với errno.EACCES (Quyền bị từ chối, lỗi 13).


573
2018-02-17 17:17



Câu trả lời được chấp nhận thực sự nguy hiểm vì nó có một điều kiện chủng tộc. Nó là đơn giản, mặc dù, vì vậy nếu bạn không biết về điều kiện chủng tộc, hoặc nghĩ rằng nó sẽ không áp dụng cho bạn, đó sẽ là lựa chọn đầu tiên rõ ràng của bạn. - Heikki Toivonen
Tăng ngoại lệ chỉ khi exception.errno != errno.EEXIST sẽ vô tình bỏ qua trường hợp khi đường dẫn tồn tại nhưng là một đối tượng không phải thư mục như một tệp. Ngoại lệ lý tưởng nên được nâng lên nếu đường dẫn là đối tượng không phải thư mục. - A-B-B
Lưu ý rằng mã trên tương đương với os.makedirs(path,exist_ok=True) - Navin
@Navin The exist_ok tham số được giới thiệu trong Python 3.2. Nó không có trong Python 2.x. Tôi sẽ kết hợp nó vào câu trả lời của tôi. - A-B-B
@HeikkiToivonen Về mặt kỹ thuật, nếu một chương trình khác đang sửa đổi các thư mục và tệp cùng lúc chương trình của bạn, toàn bộ chương trình của bạn là một điều kiện chạy đua khổng lồ. Có gì để ngăn chặn một chương trình khác chỉ xóa thư mục này sau khi mã tạo ra nó và trước khi bạn thực sự đặt các tệp vào nó? - jpmc26


Cá nhân tôi khuyên bạn nên sử dụng os.path.isdir() để kiểm tra thay vì os.path.exists().

>>> os.path.exists('/tmp/dirname')
True
>>> os.path.exists('/tmp/dirname/filename.etc')
True
>>> os.path.isdir('/tmp/dirname/filename.etc')
False
>>> os.path.isdir('/tmp/fakedirname')
False

Nếu bạn có:

>>> dir = raw_input(":: ")

Và đầu vào người dùng ngu ngốc:

:: /tmp/dirname/filename.etc

... Bạn sẽ kết thúc với một thư mục có tên filename.etc khi bạn vượt qua đối số đó os.makedirs() nếu bạn thử nghiệm với os.path.exists().


85
2018-01-14 17:57



Nếu bạn chỉ sử dụng 'isdir', bạn sẽ không gặp sự cố khi bạn cố gắng tạo thư mục và tệp có cùng tên đã tồn tại chưa? - MrWonderful
@MrWonderful Ngoại lệ kết quả khi tạo một thư mục trên một tệp hiện có sẽ phản ánh chính xác vấn đề về người gọi. - Damian Yerrick
Đây là lời khuyên xấu. Xem stackoverflow.com/a/5032238/763269. - Chris Johnson


Kiểm tra os.makedirs: (Nó đảm bảo đường dẫn đầy đủ tồn tại.)
 Để xử lý thực tế thư mục có thể tồn tại, hãy bắt OSError. (Nếu exist_ok là False (mặc định), một OSError được nâng lên nếu thư mục đích đã tồn tại.)

import os
try:
    os.makedirs('./path/to/somewhere')
except OSError:
    pass

56
2017-11-07 19:01



với try / except, bạn sẽ che dấu các lỗi trong quá trình tạo thư mục, trong trường hợp thư mục không tồn tại nhưng vì một số lý do bạn không thể tạo ra nó - Blair Conrad
Đây là cách an toàn duy nhất. - Ali Afshar
OSError sẽ được nâng lên ở đây nếu đường dẫn là tệp hoặc thư mục hiện có. Tôi đã đăng câu trả lời để giải quyết vấn đề này. - A-B-B
Đây là nửa đường. Bạn cần kiểm tra tình trạng lỗi phụ của OSError trước khi quyết định bỏ qua nó. Xem stackoverflow.com/a/5032238/763269. - Chris Johnson


Thông tin chi tiết về các chi tiết cụ thể của tình huống này

Bạn đưa ra một tệp cụ thể tại một đường dẫn nhất định và bạn kéo thư mục từ đường dẫn tệp. Sau đó, sau khi đảm bảo bạn có thư mục, bạn cố gắng mở một tệp để đọc. Để nhận xét về mã này:

filename = "/my/directory/filename.txt"
dir = os.path.dirname(filename)

Chúng tôi muốn tránh ghi đè hàm dựng sẵn, dir. Cũng thế, filepath hoặc có lẽ fullfilepath có lẽ là một tên ngữ nghĩa tốt hơn filename vì vậy điều này sẽ được viết tốt hơn:

import os
filepath = '/my/directory/filename.txt'
directory = os.path.dirname(filepath)

Mục tiêu cuối cùng của bạn là mở tệp này, ban đầu bạn viết, để viết, nhưng về cơ bản bạn đang tiếp cận mục tiêu này (dựa trên mã của bạn) như thế này, nó sẽ mở tệp cho đọc hiểu:

if not os.path.exists(directory):
    os.makedirs(directory)
f = file(filename)

Giả sử mở để đọc

Tại sao bạn sẽ tạo một thư mục cho một tệp mà bạn mong đợi ở đó và có thể đọc được?

Chỉ cần cố mở tệp.

with open(filepath) as my_file:
    do_stuff(my_file)

Nếu thư mục hoặc tệp không có ở đó, bạn sẽ nhận được IOError với một số lỗi liên quan: errno.ENOENT sẽ trỏ đến số lỗi chính xác bất kể nền tảng của bạn. Bạn có thể nắm bắt nó nếu bạn muốn, ví dụ:

import errno
try:
    with open(filepath) as my_file:
        do_stuff(my_file)
except IOError as error:
    if error.errno == errno.ENOENT:
        print 'ignoring error because directory or file is not there'
    else:
        raise

Giả sử chúng tôi đang mở văn bản

Đây là có lẽ những gì bạn đang muốn.

Trong trường hợp này, chúng tôi có lẽ không phải đối mặt với bất kỳ điều kiện chủng tộc nào. Vì vậy, chỉ cần làm như bạn, nhưng lưu ý rằng để viết, bạn cần phải mở với w chế độ (hoặc a để nối thêm). Nó cũng là một thực hành tốt nhất Python để sử dụng trình quản lý ngữ cảnh để mở tệp.

import os
if not os.path.exists(directory):
    os.makedirs(directory)
with open(filepath, 'w') as my_file:
    do_stuff(my_file)

Tuy nhiên, nói rằng chúng tôi có một số quy trình Python cố gắng để đưa tất cả dữ liệu của họ vào cùng một thư mục. Sau đó, chúng tôi có thể tranh chấp về việc tạo thư mục. Trong trường hợp đó, tốt nhất là quấn makedirs gọi trong khối try-except.

import os
import errno
if not os.path.exists(directory):
    try:
        os.makedirs(directory)
    except OSError as error:
        if error.errno != errno.EEXIST:
            raise
with open(filepath, 'w') as my_file:
    do_stuff(my_file)

29
2018-01-22 23:49





Bắt đầu từ Python 3.5, pathlib.Path.mkdir có một exist_ok cờ:

from pathlib import Path
path = Path('/my/directory/filename.txt')
path.parent.mkdir(parents=True, exist_ok=True) 
# path.parent ~ os.path.dirname(path)

Điều này đệ quy tạo thư mục và không tăng ngoại lệ nếu thư mục đã tồn tại.

(giống như os.makedirs có một exists_ok cờ bắt đầu từ python 3.2).


28
2017-12-14 16:06





Tôi đã đặt xuống dưới đây. Nó không hoàn toàn dễ dàng mặc dù.

import os

dirname = 'create/me'

try:
    os.makedirs(dirname)
except OSError:
    if os.path.exists(dirname):
        # We are nearly safe
        pass
    else:
        # There was an error on creation, so make sure we know about it
        raise

Bây giờ như tôi đã nói, điều này không thực sự dễ dàng, bởi vì chúng ta có khả năng thất bại trong việc tạo ra thư mục, và một quá trình khác tạo ra nó trong thời gian đó.


22
2017-11-07 21:23



stackoverflow.com/questions/273698/… - Ali Afshar
Hai vấn đề: (1) bạn cần kiểm tra tình trạng lỗi phụ của OSError trước khi quyết định kiểm tra os.path.exists - xem stackoverflow.com/a/5032238/763269 và (2) thành công trên os.path.exists không có nghĩa là thư mục tồn tại, chỉ là đường dẫn tồn tại - có thể là một tệp hoặc một liên kết tượng trưng hoặc đối tượng hệ thống tệp khác. - Chris Johnson


Thử os.path.exists chức năng

if not os.path.exists(dir):
    os.mkdir(dir)

22
2017-11-07 19:00



Tôi sẽ bình luận về câu hỏi này, nhưng ý chúng tôi là os.mkdir? Con trăn của tôi (2.5.2) không có os.path.mkdir .... - Blair Conrad
Không có os.path.mkdir() phương pháp. os.path module thực hiện một số hữu ích các hàm trên tên đường dẫn. - Serge S.
Đây là lời khuyên khủng khiếp. Xem stackoverflow.com/a/5032238/763269. - Chris Johnson


Kiểm tra xem một thư mục có tồn tại không và tạo nó nếu cần thiết?

Câu trả lời trực tiếp cho điều này là, giả sử một tình huống đơn giản, nơi bạn không mong đợi người dùng hoặc quy trình khác gây rối với thư mục của bạn:

if not os.path.exists(d):
    os.makedirs(d)

hoặc là nếu làm cho thư mục phải tuân theo các điều kiện chủng tộc (nghĩa là nếu sau khi kiểm tra đường dẫn tồn tại, có thể có điều gì đó khác đã thực hiện), hãy làm như sau:

import errno
try:
    os.makedirs(d)
except OSError as exception:
    if exception.errno != errno.EEXIST:
        raise

Nhưng có lẽ một cách tiếp cận tốt hơn nữa là để tránh vấn đề tranh chấp tài nguyên, bằng cách sử dụng các thư mục tạm thời thông qua tempfile:

import tempfile

d = tempfile.mkdtemp()

Dưới đây là các yếu tố cần thiết từ tài liệu trực tuyến:

mkdtemp(suffix='', prefix='tmp', dir=None)
    User-callable function to create and return a unique temporary
    directory.  The return value is the pathname of the directory.

    The directory is readable, writable, and searchable only by the
    creating user.

    Caller is responsible for deleting the directory when done with it.

Tính năng mới trong Python 3.5: pathlib.Path với exist_ok

Có một cái mới Path đối tượng (như của 3.4) với rất nhiều phương thức mà một người muốn sử dụng với các đường dẫn - một trong số đó là mkdir.

(Đối với ngữ cảnh, tôi theo dõi đại diện hàng tuần của mình bằng một tập lệnh. Đây là các phần có liên quan của mã từ tập lệnh cho phép tôi tránh nhấn vào Stack Overflow nhiều hơn một lần mỗi ngày cho cùng một dữ liệu.)

Đầu tiên nhập khẩu có liên quan:

from pathlib import Path
import tempfile

Chúng ta không phải đối phó với os.path.join bây giờ - chỉ cần tham gia các phần đường dẫn với một /:

directory = Path(tempfile.gettempdir()) / 'sodata'

Sau đó, tôi không thể đảm bảo thư mục tồn tại - exist_ok đối số hiển thị trong Python 3.5:

directory.mkdir(exist_ok=True)

Đây là phần liên quan của tài liệu:

Nếu exist_ok là đúng, FileExistsError ngoại lệ sẽ bị bỏ qua (hành vi tương tự như POSIX mkdir -p lệnh), nhưng chỉ khi thành phần đường dẫn cuối cùng không phải là tệp không phải thư mục hiện có.

Dưới đây là một chút kịch bản - trong trường hợp của tôi, tôi không phải tuân theo điều kiện chủng tộc, tôi chỉ có một quy trình dự kiến ​​thư mục (hoặc chứa các tệp) ở đó và tôi không có bất cứ điều gì cố gắng xóa cac thu mục.

todays_file = directory / str(datetime.datetime.utcnow().date())
if todays_file.exists():
    logger.info("todays_file exists: " + str(todays_file))
    df = pd.read_json(str(todays_file))

Path các đối tượng phải bị ép buộc str trước các API khác str đường dẫn có thể sử dụng chúng.

Có lẽ Pandas nên được cập nhật để chấp nhận các cá thể của lớp cơ sở trừu tượng, os.PathLike.


15
2018-01-22 23:45





Trong Python 3.4 bạn cũng có thể sử dụng thương hiệu mới pathlib mô-đun:

from pathlib import Path
path = Path("/my/directory/filename.txt")
try:
    if not path.parent.exists():
        path.parent.mkdir(parents=True)
except OSError:
    # handle error; you can also catch specific errors like
    # FileExistsError and so on.

14
2018-03-11 20:50



Nó cũng được hỗ trợ trong 2,7: pypi.python.org/pypi/pathlib - Janusz Skonieczny
@JanuszSkonieczny nó không được hỗ trợ trong Python 2.7; chỉ có nó đã được backported ở đó. Trong Python 3.4 nó là một mô-đun tích hợp. - Antti Haapala
Và sự khác biệt giữa hỗ trợ và backported là gì? Nó hoạt động phải không? Và vâng, tất cả chúng ta đều có được nó, nó không phải là thư viện py 2,7 ​​lõi, đó là lý do tại sao tôi đăng, một liên kết đến danh sách PYPI; - Janusz Skonieczny
Nếu thông tin trong các bình luận nằm trong câu trả lời, các bình luận sẽ lỗi thời. - Aaron Hall♦
@JanuszSkonieczny pypi.python.org/pypi/pathlib2 là cổng sau mới hơn. Người lớn tuổi không được duy trì. - A-B-B