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

问题描述:

如何检查要写入文件的目录是否存在,如果不存在,则使用 Python 创建目录?

HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com

解决方案1:

huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效。

在 Python ≥ 3.5 上,使用 pathlib.Path.mkdir:

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

对于旧版本的 Python,我看到两个质量很好的答案,每个都有一个小缺陷,所以我会给出我的看法:

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

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

如评论和其他地方所述,存在竞争条件 - 如果在 os.path.exists 和 os.makedirs 调用之间创建目录,则 os.makedirs 将失败并返回 OSError。不幸的是,一揽子捕获 OSError 并继续并不是万无一失的,因为它会忽略由于其他因素(例如权限不足、磁盘已满等)而导致创建目录失败的情况。

一种选择是捕获 OSError 并检查嵌入的错误代码(请参阅 Is there a cross-platform way of getting information from Python’s 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

…并通过允许 a keyword argument to os.makedirs called exist_ok(在 3.2+ 中)。

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

竞争条件很好,但 stackoverflow.com/questions/273192/#273208 中的方法将掩盖创建目录的失败。不要因为投反对票而感到难过——你不喜欢这个答案。这就是投票的目的。

请记住 os.path.exists() 不是免费的。如果正常情况是目录会存在,那么不存在的情况应作为异常处理。换句话说,尝试打开并写入您的文件,捕获 OSError 异常,并根据 errno 执行您的 makedir() 并重新尝试或重新引发。这会造成代码重复,除非您将编写内容包装在本地方法中。

os.path.exists 还为文件返回 True。我已经发布了一个答案来解决这个问题。

正如此处其他答案的评论者所指出的,自 Python 3.2 以来,os.makedirs() 的 exists_ok 参数可用于涵盖如何处理路径的先前存在。

如果路径分隔符被意外遗漏,os.mkdirs() 可能会创建意外文件夹,当前文件夹与预期不符,路径元素包含路径分隔符。如果您使用 os.mkdir(),这些错误将引发异常,提醒您它们的存在。

解决方案2:

huntsbot.com – 高效赚钱,自由工作

Python 3.5+:

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

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

Python 3.2+:

使用 pathlib:

如果可以,请安装名为 pathlib2 的当前 pathlib 反向端口。不要安装名为 pathlib 的较旧的未维护反向端口。接下来,参考上面的 Python 3.5+ 部分,同样使用它。

如果使用 Python 3.4,即使它带有 pathlib,它也缺少有用的 exist_ok 选项。向后移植旨在提供一个更新的、更高级的 mkdir 实现,其中包括这个缺失的选项。

使用 os:

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

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

Python 2.7+:

使用 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 创建嵌套目录,如果该目录已存在,则不执行任何操作。这适用于 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()”中,因为我希望该目录几乎每次都存在,并且我可以通过这种方式避免异常。

@CharlesL。如果您的原因是性能,则异常可能比检查的磁盘 IO 便宜。

@jpmc26 但 makedirs 在仅检查抛出 OSError 时会执行额外的 stat、umask、lstat。

这是错误的答案,因为它引入了潜在的 FS 种族条件。请参阅 Aaron Hall 的回答。

正如@sleepycal 所说,这与接受的答案有类似的竞争条件。如果在引发错误和检查 os.path.isdir 之间其他人删除了该文件夹,您将引发该文件夹存在的错误、过时和令人困惑的错误。

解决方案3:

huntsbot.com – 高效赚钱,自由工作

使用 try except 和来自 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

换句话说,我们尝试创建目录,但如果它们已经存在,我们将忽略错误。另一方面,报告任何其他错误。例如,如果您事先创建 dir ‘a’ 并从中删除所有权限,您将收到一个 errno.EACCES 引发的 OSError(权限被拒绝,错误 13)。

接受的答案实际上是危险的,因为它具有竞争条件。不过,它更简单,所以如果你不知道比赛条件,或者认为它不适用于你,那显然是你的首选。

仅当 exception.errno != errno.EEXIST 会无意中忽略路径存在但为非目录对象(例如文件)时的情况,才引发异常。如果路径是非目录对象,则理想情况下应该引发异常。

请注意,上面的代码等价于 os.makedirs(path,exist_ok=True)

@Navin exist_ok 参数是在 Python 3.2 中引入的。它在 Python 2.x 中不存在。我会将其纳入我的答案中。

@HeikkiToivonen 从技术上讲,如果另一个程序正在修改您的程序的目录和文件,那么您的整个程序就是一个巨大的竞争条件。在代码创建它之后和实际将文件放入其中之前,如何阻止另一个程序删除该目录?

解决方案4:

huntsbot.com精选全球7大洲远程工作机会,涵盖各领域,帮助想要远程工作的数字游民们能更精准、更高效的找到对方。

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

这会递归地创建目录,如果目录已经存在,则不会引发异常。

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

注意:当我发布此答案时,没有提到其他答案exist_ok …

解决方案5:

huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求

我个人建议您使用 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

…如果您使用 os.path.exists() 进行测试,当您将该参数传递给 os.makedirs() 时,您最终会得到一个名为 filename.etc 的目录。

解决方案6:

huntsbot.com – 程序员副业首选,一站式外包任务、远程工作、创意产品分享订阅平台。

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

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

使用 try/except,您将掩盖目录创建中的错误,如果目录不存在但由于某种原因您无法创建它

解决方案7:

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

试试 os.path.exists 函数

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

解决方案8:

保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com

关于这种情况的具体情况的见解

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

文件名 = “/my/directory/filename.txt” 目录 = os.path.dirname(文件名)

我们希望避免覆盖内置函数 dir。此外,filepath 或 fullfilepath 可能是比 filename 更好的语义名称,所以这样写会更好:

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

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

如果不是 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)

解决方案9:

一个优秀的自由职业者,应该有对需求敏感和精准需求捕获的能力,而huntsbot.com提供了这个机会

我已经把以下内容。不过,这也不是万无一失的。

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

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

解决方案10:

huntsbot.com全球7大洲远程工作机会,探索不一样的工作方式

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

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

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) 用户可调用函数,用于创建并返回唯一的临时目录。返回值是目录的路径名。该目录只能由创建用户读取、写入和搜索。调用者负责在完成后删除目录。

Python 3.5 中的新功能:pathlib.Path 和 exists_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)

以下是 documentation 的相关部分:

如果exist_ok 为真,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))

必须将 Path 对象强制转换为 str,然后其他需要 str 路径的 API 才能使用它们。

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

解决方案11:

huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感

在 Python 3.4 中,您还可以使用 brand new pathlib module:

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.

原文链接:https://www.huntsbot.com/qa/Pdg2/how-can-i-safely-create-a-nested-directory?lang=zh_CN

huntsbot.com提供全网独家一站式外包任务、远程工作、创意产品分享与订阅服务!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值