如何创建目录及缺失的父目录?
我该怎么做才能在指定的路径下创建一个文件夹,并且同时创建这个路径中缺失的所有上级文件夹呢?比如,Bash命令 mkdir -p /path/to/nested/directory
就可以做到这一点。
27 个回答
使用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)。
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,如果你对此感兴趣,请参考相关文档。
在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
,并检查里面的错误代码(可以参考 有没有跨平台的方法来获取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.