如何创建目录及缺失的父目录?

5650 投票
27 回答
3713658 浏览
提问于 2025-04-11 18:55

我该怎么做才能在指定的路径下创建一个文件夹,并且同时创建这个路径中缺失的所有上级文件夹呢?比如,Bash命令 mkdir -p /path/to/nested/directory 就可以做到这一点。

27 个回答

657

使用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

换句话说,我们尝试创建目录,但如果目录已经存在,我们就忽略这个错误。另一方面,其他任何错误都会被报告出来。例如,如果你提前创建了一个名为'a'的目录,并且把它的所有权限都去掉了,那么你会遇到一个OSError错误,错误代码是errno.EACCES(权限被拒绝,错误代码13)。

1542

Python 3.5及以上版本:

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

pathlib.Path.mkdir 这个方法可以递归地创建目录,如果目录已经存在,它不会报错。如果你不需要创建父目录,可以省略 parents 这个参数。

Python 3.2及以上版本:

使用 pathlib

如果可以的话,安装当前的 pathlib 版本,叫做 pathlib2。不要安装旧的、不再维护的版本 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

如果可以的话,安装当前的 pathlib 版本,叫做 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 中都可以使用。不过需要注意的是,distutils 已被弃用,计划在 Python 3.12 中移除。

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

根据 Bug 10948,这个替代方案的一个严重限制是它在给定路径的情况下,每个 Python 进程只能使用一次。换句话说,如果你用它创建了一个目录,然后从 Python 内部或外部删除了这个目录,再次使用 mkpath 重新创建同一个目录时,mkpath 会默默地使用它之前创建目录的无效缓存信息,而不会真正再创建目录。相比之下,os.makedirs 不依赖于任何这样的缓存。这个限制对于某些应用来说可能是可以接受的。


关于目录的 mode,如果你对此感兴趣,请参考相关文档。

7077

在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.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.makedirs 时使用一个名为 exist_ok 的关键字参数(在3.2及以上版本中)。

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

撰写回答