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

半世苍凉 提交于 2019-12-06 02:52:21

检查文件目录是否存在的最优雅方法是什么?如果不存在,则使用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)

是否有“打开”标志,使它自动发生?


#1楼

Python 3.5以上版本:

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

pathlib.Path.mkdir使用的pathlib.Path.mkdir递归创建目录,如果目录已经存在,则不会引发异常。 如果不需要或不希望创建parents ,请跳过“ parents参数。

Python 3.2以上版本:

使用pathlib

如果可以,请安装名为pathlib2的当前pathlib pathlib2 。 不要安装名为pathlib的较旧的未维护的pathlib 。 接下来,请参考上面的Python 3.5+部分,并对其进行相同的使用。

如果使用Python 3.4,即使pathlib附带了pathlib ,它也会缺少有用的exist_ok选项。 反向端口旨在提供mkdir的更新和更高级的实现,其中包括此缺少的选项。

使用os

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

os.makedirs使用的os.makedirs递归创建目录,如果目录已经存在,则不会引发异常。 仅当使用Python 3.2+时,它才具有可选的exist_ok参数,默认值为False 。 在2.7之前的Python 2.x中不存在此参数。 这样,就无需像Python 2.7那样进行手动异常处理。

Python 2.7+:

使用pathlib

如果可以,请安装名为pathlib2的当前pathlib pathlib2 。 不要安装名为pathlib的较旧的未维护的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创建嵌套目录,如果目录已经存在, mkpath执行任何操作。 这适用于Python 2和3。

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

根据Bug 10948 ,此替代方案的严重局限性在于,对于给定路径,每个python进程仅工作一次。 换句话说,如果使用它来创建目录,然后从Python内部或外部删除目录,然后再次使用mkpath来重新创建同一目录,则mkpath会简单地静默使用其先前已创建目录的无效缓存信息,并且实际上不会再次创建目录。 相反, os.makedirs不依赖任何此类缓存。 对于某些应用程序,此限制可能是可以的。


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


#2楼

相关的Python文档建议使用EAFP编码样式(比许可更容易获得宽恕) 。 这意味着代码

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

该文档正是由于此问题中讨论的种族条件而提出了这一建议。 此外,正如此处其他人所提到的,查询一次操作系统而不是两次查询操作系统具有性能优势。 最后,在某些情况下(当开发人员知道应用程序正在运行的环境时),可能会提出支持第二个代码的参数,只有在特殊情况下才提倡该程序已为该程序建立了私有环境。本身(以及同一程序的其他实例)。

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


#3楼

尝试os.path.exists函数

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

#4楼

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

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

#5楼

我看到两个质量很好的答案,每个都有一个小缺陷,因此我将对此进行说明:

尝试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 (在FileExistsError版本中)都FileExistsError改善了此代码。

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

...,并允许os.makedirs的关键字参数称为exist_ok (在3.2+中)。

os.makedirs("path/to/directory", exist_ok=True)  # succeeds even if directory exists.
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!