如何在Python中安全地创建嵌套目录?

Parand 3909 python directory operating-system exception path

检查文件目录是否存在的最优雅方法是什么,如果不存在,使用Python创建目录?这是我尝试过的:

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)

不知何故,我错过了os.path.exists(感谢kanja,Blair和Douglas).这就是我现在拥有的:

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

是否有"开放"的标志,这会自动发生?

Blair Conrad.. 4767

我看到两个具有良好品质的答案,每个答案都有一个小缺陷,所以我会考虑它:

尝试os.path.exists,并考虑os.makedirs创建.

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

正如评论和其他地方所述,存在竞争条件 - 如果os.path.existsos.makedirs调用和调用之间创建目录,os.makedirs则将失败OSError.不幸的是,全面捕捉OSError和继续并非万无一失,因为它会忽略由于其他因素(例如权限不足,完整磁盘等)而无法创建目录.

一种选择是捕获OSError并检查嵌入的错误代码(请参阅是否存在从Python的OSError获取信息的跨平台方式):

import os, errno

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

或者,可能会有第二个os.path.exists,但假设另一个在第一次检查后创建了目录,然后在第二次检查之前将其删除 - 我们仍然可能被愚弄.

根据应用程序,并发操作的危险可能多于或少于文件权限等其他因素造成的危险.在选择实现之前,开发人员必须更多地了解正在开发的特定应用程序及其预期环境.

现代版本的Python通过暴露FileExistsError(在3.3 +中)改进了这段代码......

try:
    os.makedirs("path/to/directory")
except FileExistsError:
    # directory already exists
    pass

...并允许调用关键字参数os.makedirsexist_ok(在3.2+中).

os.makedirs("path/to/directory", exist_ok=True)  # succeeds even if directory exists.

  • 请记住,os.path.exists()不是免费的.如果正常情况是目录将存在,那么不应将其作为例外处理.换句话说,尝试打开并写入您的文件,捕获OSError异常,并根据errno,执行您的makedir()并重新尝试或重新加注.这会创建代码重复,除非您在本地方法中包装写入. (26认同)
  • `os.path.exists`也为文件返回`True`.我已经发布了一个答案来解决这个问题. (21认同)
  • 正如其他答案的评论者所指出的那样,`os.makedirs()`的`exists_ok`参数可用于涵盖从Python 3.2开始如何处理路径的先前存在. (13认同)
  • `os.mkdirs()`如果意外遗漏了路径分隔符,可以创建非预期的文件夹,当前文件夹不是预期的,路径元素包含路径分隔符.如果你使用`os.mkdir()`这些错误会引发异常,提醒你它们的存在. (6认同)
  • 竞争条件是一个好点,但http://stackoverflow.com/questions/273192/#273208中的方法将掩盖创建目录的失败.投票不要心疼 - 你不喜欢这个答案.这是投票的目的. (5认同)
  • 太糟糕了,这被标记为正确并被提升至1000.你可以,并且应该捕获errno.EEXIST (2认同)

Acumenus.. 1177

Python 3.5+:

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

pathlib.Path.mkdir如上所用,递归创建目录,如果目录已存在,则不会引发异常.如果您不需要或希望创建父项,请跳过该parents参数.

Python 3.2+:

使用pathlib:

如果可以,请安装pathlib名为的当前backport pathlib2.不要安装名为的旧的非维护后端口pathlib.接下来,请参阅上面的Python 3.5+部分并使用它.

如果使用Python 3.4,即使它附带pathlib,它也缺少有用的exist_ok选项.后端旨在提供更新,更优越的实现,mkdir其中包括此缺失选项.

使用os:

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

os.makedirs如上所用,递归创建目录,如果目录已存在,则不会引发异常.它exist_ok只有在使用Python 3.2+时才有可选参数,默认值为False.这个参数在Python 2.x中不存在,最高可达2.7.因此,不需要像Python 2.7那样进行手动异常处理.

Python 2.7+:

使用pathlib:

如果可以,请安装pathlib名为的当前backport pathlib2.不要安装名为的旧的非维护后端口pathlib.接下来,请参阅上面的Python 3.5+部分并使用它.

使用os:

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

而幼稚溶液可首先使用os.path.isdir,接着os.makedirs,在溶液上方反转两个操作的顺序.这样做可以防止常见的竞争条件与创建目录的重复尝试有关,并且还可以消除目录中的文件歧义.

请注意,捕获异常和使用errno的用处有限,因为OSError: [Errno 17] File exists,即为errno.EEXIST文件和目录引发.仅检查目录是否存在更可靠.

替代方案:

mkpath创建嵌套目录,如果该目录已存在则不执行任何操作.这适用于Python 2和3.

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

根据Bug 10948,这种替代方案的一个严重限制是,对于给定路径,每个python进程只能运行一次.换句话说,如果你用它来创建一个目录,那么从Python里面或外面删除目录,然后mkpath再次使用来重新创建同一个目录,mkpath只是默默地使用它以前创建目录的无效缓存信息,而不会实际上再次创建目录.相反,os.makedirs不依赖于任何此类缓存.对于某些应用,此限制可能没问题.


关于目录的模式,如果您关心它,请参阅文档.

  • 就我所知,这个答案涵盖了几乎所有特殊情况.我计划将其包装在"if not os.path.isdir()"中,但因为我希望目录几乎每次都存在,我可以避免这种异常. (13认同)
  • @CharlesL.如果您的原因是性能,则异常可能比检查的磁盘IO便宜. (4认同)
  • 这是错误的答案,因为它引入了潜在的FS竞赛cond.查看Aaron Hall的回答. (3认同)
  • 正如@sleepycal所说,这种情况与公认的答案相似.如果在引发错误和检查`os.path.isdir`之间有人删除了该文件夹,则会引发该文件夹存在的错误,过时和混乱错误. (3认同)

Heikki Toivo.. 600

使用try除了和errno模块的正确错误代码摆脱了竞争条件并且是跨平台的:

import os
import errno

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

换句话说,我们尝试创建目录,但如果它们已经存在,我们会忽略错误.另一方面,报告任何其他错误.例如,如果创建目录"A"事先并删除了这一切的权限,你会得到一个OSError有凸起errno.EACCES(权限被拒绝,错误13).

  • 请注意,上面的代码相当于`os.makedirs(path,exist_ok = True)` (176认同)
  • @Navin在Python 3.2中引入了`exist_ok`参数.它在Python 2.x中不存在.我会把它纳入我的答案. (57认同)
  • @HeikkiToivonen从技术上讲,如果另一个程序在你的程序同时修改目录和文件,你的整个程序就是一个巨大的竞争条件.什么是阻止另一个程序在代码创建之后以及在实际放入文件之前删除此目录? (26认同)
  • 接受的答案实际上是危险的,因为它有竞争条件.但是,它更简单,所以如果你不知道竞争条件,或者认为它不适用于你,那将是你明显的首选. (24认同)
  • 仅当`exception.errno!= errno.EEXIST`时,无意中忽略路径存在但是非目录对象(如文件)的情况下引发异常.如果路径是非目录对象,则理想情况下应该引发异常. (15认同)
  • *对此*的常见原因是什么?即:为什么人们更喜欢if语句到try/except?如果您错过了错误,那么尝试/除了更危险吗? (2认同)
  • @ jpmc26,从技术上讲,可以使用以下一个或多个:权限,所有权,咨询锁,强制锁. (2认同)
  • @ABB所有权只有在您不需要阅读相同文件的应用程序的不同实例时才会起作用,在这种情况下,您可以通过仅仅意识到您正在访问的内容来避免没有它的竞争条件.权限相同.是否存在创建目录和获取锁定的原子方法?如果没有,那么这对这里提出的具体案例没有帮助.如果是这样,这个解决方案不包含它,这意味着它和另一个一样"危险". (2认同)
  • Downvoted.`mkdirs`的源代码实际上警告*反对*检查EEXIST:https://github.com/python/cpython/blob/e304e33c16e060932d1e2cc8a030d42b02b429b5/Lib/os.py#L195 (2认同)

小智.. 100

我个人建议你os.path.isdir()用来测试而不是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

如果你有:

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

一个愚蠢的用户输入:

:: /tmp/dirname/filename.etc

... 如果你测试的话,你最终会得到一个filename.etc在你传递参数时命名的目录.os.makedirs()os.path.exists()

  • 如果仅使用'isdir',当您尝试创建目录并且已存在同名文件时,是否仍然存在问题? (8认同)
  • @MrWonderful在现有文件上创建目录时产生的异常会将问题正确反映回调用者. (3认同)

Douglas Mayl.. 76

检查os.makedirs :(确保存在完整的路径.)
要处理目录可能存在的事实,请捕获OSError.(如果exist_ok为False(默认值),则在目标目录已存在的情况下引发OSError.)

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

  • 使用try/except,你将掩盖目录创建中的错误,如果目录不存在但由于某种原因你不能成功 (19认同)
  • 这是中途.在确定忽略它之前,您需要检查"OSError"的子错误情况.请参见http://stackoverflow.com/a/5032238/763269. (4认同)
  • 如果路径是现有文件或目录,则会在此处引发OSError。我已经发布了一个答案来解决这个问题。 (2认同)

hiro protago.. 68

从Python 3.5开始,pathlib.Path.mkdir有一个exist_ok标志:

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

这会以递归方式创建目录,如果目录已存在,则不会引发异常.

(就像从python 3.2开始os.makedirs得到一个exist_ok标志,例如os.makedirs(path, exist_ok=True))


Aaron Hall.. 42

洞察这种情况的具体情况

您在特定路径中提供特定文件,然后从文件路径中提取目录.然后在确保您拥有该目录后,尝试打开文件进行读取.要评论此代码:

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

我们希望避免覆盖内置函数dir.此外,filepath或者可能fullfilepath是一个更好的语义名称,filename因此写得更好:

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

你的最终目标是打开这个文件,你最初说,写,但你基本上接近这个目标(基于你的代码),这样打开文件阅读:

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

假设开放阅读

为什么要为您希望在那里并且能够阅读的文件创建一个目录?

只是尝试打开文件.

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

如果目录或文件不在那里,您将得到一个IOError带有相关错误编号:errno.ENOENT无论您的平台如何,都将指向正确的错误编号.如果你愿意,你可以抓住它,例如:

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

假设我们正在写作

可能就是你想要的.

在这种情况下,我们可能没有遇到任何竞争条件.所以就像你一样,但请注意,对于写作,你需要打开w模式(或a追加).使用上下文管理器打开文件也是Python的最佳实践.

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

但是,假设我们有几个Python进程试图将所有数据放入同一目录中.然后我们可能会争论创建目录.在这种情况下,最好将makedirs调用包装在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)


gone.. 33

试试这个os.path.exists功能

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

  • 我打算对这个问题发表评论,但我们的意思是os.mkdir吗?我的python(2.5.2)没有os.path.mkdir .... (3认同)
  • 这是一个糟糕的建议.请参见http://stackoverflow.com/a/5032238/763269. (3认同)

Ali Afshar.. 30

我把以下内容放下了.但这并非完全万无一失.

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

现在正如我所说,这并非万无一失,因为我们有可能无法创建目录,而另一个进程在此期间创建它.


Aaron Hall.. 24

检查目录是否存在并在必要时创建它?

对此的直接回答是,假设您不希望其他用户或进程弄乱您的目录的简单情况:

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

或者如果使目录受到竞争条件的影响(即,如果检查路径存在后,还有其他东西可能已经成功),请执行以下操作:

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

但也许更好的方法是通过以下方式使用临时目录来回避资源争用问题tempfile:

import tempfile

d = tempfile.mkdtemp()

以下是在线文档的基本要点:

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.

Python 3.5中的新功能:pathlib.Pathwithexist_ok

有一个新Path对象(截至3.4),有许多方法需要与路径一起使用 - 其中一个是mkdir.

(对于上下文,我用脚本跟踪我的每周代表.这是脚本中代码的相关部分,允许我避免每天多次针对相同的数据访问Stack Overflow.)

首先是相关进口:

from pathlib import Path
import tempfile

我们现在不必处理os.path.join- 只需使用以下命令连接路径部分/:

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

然后我自觉地确保目录存在 - exist_ok参数显示在Python 3.5中:

directory.mkdir(exist_ok=True)

这是文档的相关部分:

如果exist_ok为true,FileExistsError则将忽略异常(与POSIX mkdir -p命令相同的行为),但仅限于最后一个路径组件不是现有的非目录文件.

这里有一些脚本 - 在我的情况下,我不受竞争条件的限制,我只有一个进程需要目录(或包含文件),并且我没有任何东西试图删除目录.

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

Pathstr在期望str路径可以使用它们的其他API之前,必须强制对象.

也许应该更新Pandas以接受抽象基类的实例os.PathLike.


Antti Haapal.. 21

在Python 3.4中,您还可以使用全新的pathlib模块:

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.


kavadias.. 13

相关的Python文档建议使用的编码风格EAFP(更容易原谅比许可ASK) .这意味着代码

try:
    os.makedirs(path)
except OSError as exception:
    if exception.errno != errno.EEXIST:
        raise
    else:
        print "\nBE CAREFUL! Directory %s already exists." % path

比替代方案更好

if not os.path.exists(path):
    os.makedirs(path)
else:
    print "\nBE CAREFUL! Directory %s already exists." % path

文档建议这正是因为这个问题中讨论的竞争条件.此外,正如其他人在此提到的那样,查询一次而不是两次操作系统有一个性能优势.最后,在某些情况下 - 当开发人员知道应用程序运行的环境时 - 可能会支持第二个代码的论点 - 只能在程序为其设置私有环境的特殊情况下提倡这个论点.本身(以及同一程序的其他实例).

即使在这种情况下,这也是一种不好的做法,可能导致长时间无用的调试.例如,我们为目录设置权限的事实不应该让我们为我们的目的设置适当的印象权限.可以使用其他权限挂载父目录.通常,程序应始终正常工作,程序员不应期望一个特定的环境.


euccas.. 11

Python3中,os.makedirs支持设置exist_ok.默认设置为False,这表示OSError如果目标目录已存在,将引发该设置.通过设置exist_okTrue,OSError(目录存在)将被忽略,并且不会创建目录.

os.makedirs(path,exist_ok=True)

Python2中,os.makedirs不支持设置exist_ok.您可以在heikki-toivonen的答案中使用该方法:

import os
import errno

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


Dennis Golom.. 9

您可以使用 mkpath

# Create a directory and any missing ancestor directories. 
# If the directory already exists, do nothing.

from distutils.dir_util import mkpath
mkpath("test")    

请注意,它也将创建祖先目录.

它适用于Python 2和3.

  • `distutils.dir_util`不是distutil公共API的一部分,在多线程环境中存在问题:http://bugs.python.org/issue10948 (2认同)

tashuhka.. 8

对于单线解决方案,您可以使用IPython.utils.path.ensure_dir_exists():

from IPython.utils.path import ensure_dir_exists
ensure_dir_exists(dir)

文档中:确保存在目录.如果它不存在,尝试创建它并防止竞争条件,如果另一个进程正在做同样的事情.

  • 绝对不能保证存在“ IPython”模块。它本地存在于我的Mac上,但不存在于我的任何Linux安装的Python中。基本上,它不是[Python模块索引](https://docs.python.org/py-modindex.html)中列出的模块之一。 (2认同)

小智.. 8

我使用os.path.exists(),是一个Python 3脚本,可用于检查目录是否存在,如果它不存在则创建一个,如果存在则删除它(如果需要).

它会提示用户输入目录,并且可以轻松修改.


alissonmulle.. 6

我看到Heikki ToivonenABB的答案并想到了这种变化.

import os
import errno

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


小智.. 6

你可以使用os.listdir这个:

import os
if 'dirName' in os.listdir('parentFolderPath')
    print('Directory Exists')


Victoria Stu.. 6

我发现这个Q/A,我最初对一些失败和错误感到困惑.我正在使用Python 3(在Arch Linux x86_64系统上的Anaconda虚拟环境中的v.3.5).

考虑这个目录结构:

??? output/         ## dir
   ??? corpus       ## file
   ??? corpus2/     ## dir
   ??? subdir/      ## dir

这是我的实验/笔记,它澄清了一些事情:

# ----------------------------------------------------------------------------
# [1] /sf/ask/17360801/

import pathlib

""" Notes:
        1.  Include a trailing slash at the end of the directory path
            ("Method 1," below).
        2.  If a subdirectory in your intended path matches an existing file
            with same name, you will get the following error:
            "NotADirectoryError: [Errno 20] Not a directory:" ...
"""
# Uncomment and try each of these "out_dir" paths, singly:

# ----------------------------------------------------------------------------
# METHOD 1:
# Re-running does not overwrite existing directories and files; no errors.

# out_dir = 'output/corpus3'                ## no error but no dir created (missing tailing /)
# out_dir = 'output/corpus3/'               ## works
# out_dir = 'output/corpus3/doc1'           ## no error but no dir created (missing tailing /)
# out_dir = 'output/corpus3/doc1/'          ## works
# out_dir = 'output/corpus3/doc1/doc.txt'   ## no error but no file created (os.makedirs creates dir, not files!  ;-)
# out_dir = 'output/corpus2/tfidf/'         ## fails with "Errno 20" (existing file named "corpus2")
# out_dir = 'output/corpus3/tfidf/'         ## works
# out_dir = 'output/corpus3/a/b/c/d/'       ## works

# [2] https://docs.python.org/3/library/os.html#os.makedirs

# Uncomment these to run "Method 1":

#directory = os.path.dirname(out_dir)
#os.makedirs(directory, mode=0o777, exist_ok=True)

# ----------------------------------------------------------------------------
# METHOD 2:
# Re-running does not overwrite existing directories and files; no errors.

# out_dir = 'output/corpus3'                ## works
# out_dir = 'output/corpus3/'               ## works
# out_dir = 'output/corpus3/doc1'           ## works
# out_dir = 'output/corpus3/doc1/'          ## works
# out_dir = 'output/corpus3/doc1/doc.txt'   ## no error but creates a .../doc.txt./ dir
# out_dir = 'output/corpus2/tfidf/'         ## fails with "Errno 20" (existing file named "corpus2")
# out_dir = 'output/corpus3/tfidf/'         ## works
# out_dir = 'output/corpus3/a/b/c/d/'       ## works

# Uncomment these to run "Method 2":

#import os, errno
#try:
#       os.makedirs(out_dir)
#except OSError as e:
#       if e.errno != errno.EEXIST:
#               raise
# ----------------------------------------------------------------------------

结论:在我看来,"方法2"更加健壮.

[1] 如果目录不存在,如何创建目录?

[2] https://docs.python.org/3/library/os.html#os.makedirs


归档时间:

查看次数:

2450871 次

最近记录:

1 年,2 月 前