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

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

3446017 次浏览

尝试#0功能

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

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

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

在Python≥3.5上,使用#0

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

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

尝试#0,并考虑#1的创作。

import osif 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通过公开#0(在3.3+中)大大改进了这段代码…

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

…并允许#0的关键字参数称为#1(在3.2+中)。

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

我已经记下了以下内容。但这并不是万无一失的。

import os
dirname = 'create/me'
try:os.makedirs(dirname)except OSError:if os.path.exists(dirname):# We are nearly safepasselse:# There was an error on creation, so make sure we know about itraise

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

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

如果您有:

>>> directory = raw_input(":: ")

一个愚蠢的用户输入:

:: /tmp/dirname/filename.etc

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

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

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

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

Python 3.5+:

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

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

Python 3.2+:

使用pathlib

如果可以,请安装当前名为#1pathlib反向端口。不要安装名为#0的旧的未维护反向端口。接下来,请参阅上面的Python 3.5+部分并使用它。

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

使用os

import osos.makedirs(path, exist_ok=True)

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

Python 2.7+:

使用pathlib

如果可以,请安装当前名为#1pathlib反向端口。不要安装名为#0的旧的未维护反向端口。接下来,请参阅上面的Python 3.5+部分并使用它。

使用os

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

虽然简单的解决方案可能首先使用#0,然后使用#1,但上述解决方案颠倒了两个操作的顺序。这样做,它防止了与重复尝试创建目录有关的常见竞争条件,并且还消除了目录中的文件。

请注意,捕获异常并使用errno的有用性有限,因为OSError: [Errno 17] File exists,即errno.EEXIST,是为文件和目录引发的。

备选案文:

#0创建嵌套目录,如果目录已经存在,则不执行任何操作。这在Python 2和3中都有效。

import distutils.dir_utildistutils.dir_util.mkpath(path)

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


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

相关Python留档建议使用EAFP编码风格(请求原谅比请求许可更容易)。这意味着代码

try:os.makedirs(path)except OSError as exception:if exception.errno != errno.EEXIST:raiseelse: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

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

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

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

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

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

如果创建目录受竞争条件的约束(即如果在检查路径存在之后,其他东西可能已经创建了它),请执行以下操作:

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

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

import tempfile
d = tempfile.mkdtemp()

以下是在线文档的要点:

mkdtemp(suffix='', prefix='tmp', dir=None)User-callable function to create and return a unique temporarydirectory.  The return value is the pathname of the directory.
The directory is readable, writable, and searchable only by thecreating user.
Caller is responsible for deleting the directory when done with it.

Python 3.5中的新功能:pathlib.Path withexist_ok

有一个新的Path对象(从3.4开始),其中包含许多想要与路径一起使用的方法-其中一个是mkdir

(为了上下文,我正在使用脚本跟踪我的每周代表。以下是脚本中的相关代码部分,允许我避免每天对相同的数据多次点击Stack Overflow。)

首先是相关进口:

from pathlib import Pathimport 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))

Path对象必须强制为str,然后其他期望str路径的API才能使用它们。

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

对这种情况的具体情况的见解

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

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

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

import osfilepath = '/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 errnotry: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 osif not os.path.exists(directory):os.makedirs(directory)with open(filepath, 'w') as my_file:do_stuff(my_file)

但是,假设我们有几个Python进程试图将它们的所有数据放入同一个目录中。那么我们可能会在目录的创建上发生争用。在这种情况下,最好将makedirs调用包装在一个try除外块中。

import osimport errnoif not os.path.exists(directory):try:os.makedirs(directory)except OSError as error:if error.errno != errno.EEXIST:raisewith open(filepath, 'w') as my_file:do_stuff(my_file)

在Python 3.4中,您也可以使用全新的#0模块

from pathlib import Pathpath = 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.

我看到了Heikki ToivonenA-B-B的答案,并想到了这种变化。

import osimport 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

对于单行解决方案,您可以使用#0

from IPython.utils.path import ensure_dir_existsensure_dir_exists(dir)

留档确保目录存在。如果它不存在,请尝试创建它并在另一个进程正在执行相同操作时防止竞争条件。

IPython是一个扩展包,不是标准库的一部分。

你可以使用os.listdir

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

你可以使用#0

# Create a directory and any missing ancestor directories.# If the directory already exists, do nothing.
from distutils.dir_util import mkpathmkpath("test")

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

它适用于Python 2和3。

如果你考虑以下几点:

os.path.isdir('/tmp/dirname')

意味着目录(路径)存在并且是目录。所以对我来说,这种方式满足了我的需要。所以我可以确保它是文件夹(不是文件)并且存在。

从Python 3.5开始,#0有一个exist_ok标志:

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

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

(就像#0从python 3.2开始获得exist_ok标志,例如os.makedirs(path, exist_ok=True)


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

python3中,os.makedirs支持设置exist_ok。默认设置为False,这意味着如果目标目录已经存在,将引发OSError。通过将exist_ok设置为TrueOSError(目录存在)将被忽略并且不会创建目录。

os.makedirs(path,exist_ok=True)

python2中,os.makedirs不支持设置exist_ok。您可以使用heikki-toivonen的回答中的方法:

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

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

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

我在Python中使用目录时遇到的一些失败和错误让我感到困惑,之后我找到了这个Q/A。我正在使用Python 3(在ArchLinuxx86_64系统上的Anaconda虚拟环境中的3.5版)。

考虑这个目录结构:

└── output/         ## dir├── corpus       ## file├── corpus2/     ## dir└── subdir/      ## dir

以下是我的实验/笔记,它提供了澄清:

# ----------------------------------------------------------------------------# [1] https://stackoverflow.com/questions/273192/how-can-i-create-a-directory-if-it-does-not-exist
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 filewith 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

使用此命令检查并创建目录

 if not os.path.isdir(test_img_dir):os.mkdir(test_img_dir)

在程序/项目的切入点调用函数create_dir()

import os
def create_dir(directory):if not os.path.exists(directory):print('Creating Directory '+directory)os.makedirs(directory)
create_dir('Project directory')

如果在支持命令的机器上运行,为什么不使用子进程模块mkdir-p选项?适用于python 2.7和python 3.6

from subprocess import callcall(['mkdir', '-p', 'path1/path2/path3'])

应该在大多数系统上做这个把戏。

在可移植性无关紧要的情况下(例如,使用docker),解决方案是干净的2行。您也不必添加逻辑来检查目录是否存在。最后,重新运行是安全的,没有任何副作用

如果您需要错误处理:

from subprocess import check_calltry:check_call(['mkdir', '-p', 'path1/path2/path3'])except:handle...

您必须在创建目录之前设置完整路径:

import os,sys,inspectimport pathlib
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))your_folder = currentdir + "/" + "your_folder"
if not os.path.exists(your_folder):pathlib.Path(your_folder).mkdir(parents=True, exist_ok=True)

这对我有用,希望它对你也有效

这可能并不能完全回答这个问题。但我猜您的真正意图是创建一个文件及其父目录,给定其内容全部在1个命令中。

您可以使用fastcore扩展到Pathlib:path.mk_write(data)来做到这一点

from fastcore.utils import PathPath('/dir/to/file.txt').mk_write('Hello World')

查看更多Fastcore留档

如果您要将文件写入可变路径,您可以在文件的路径上使用它来确保创建父目录。

from pathlib import Path
path_to_file = Path("zero/or/more/directories/file.ext")parent_directory_of_file = path_to_file.parentparent_directory_of_file.mkdir(parents=True, exist_ok=True)

即使path_to_filefile.ext(零目录深度)也有效。

pathlib.PurePath.parentpathlib.Path.mkdir

在python中执行此操作的最佳方法

#Devilimport osdirectory = "./out_dir/subdir1/subdir2"if not os.path.exists(directory):os.makedirs(directory)

最快最安全的方法是:如果不存在则创建,如果存在则跳过:

from pathlib import PathPath("path/with/childs/.../").mkdir(parents=True, exist_ok=True)