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

时间:2008-11-07 18:56:46

标签: python exception path directory operating-system

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

是否有“开放”的标志,这会自动发生?

28 个答案:

答案 0 :(得分:4379)

我看到两个具有良好品质的答案,每个答案都有一个小缺陷,所以我会把它放在上面:

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

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

正如评论和其他地方所述,存在竞争条件 - 如果目录是在os.path.existsos.makedirs来电之间创建的,os.makedirs将失败并显示{{1} }。不幸的是,全面捕捉OSError并继续不是万无一失的,因为它会忽略由于其他因素(例如权限不足,完整磁盘等)而无法创建目录。

一个选项是捕获OSError并检查嵌入的错误代码(请参阅Is there a cross-platform way of getting information from Python’s OSError):

OSError

或者,可能会有第二个import os, errno try: os.makedirs(directory) except OSError as e: if e.errno != errno.EEXIST: raise ,但假设另一个在第一次检查后创建了目录,然后在第二次检查之前将其删除 - 我们仍然可以被愚弄。

根据应用程序的不同,并发操作的危险可能大于或等于文件权限等其他因素造成的危险。在选择实现之前,开发人员必须更多地了解正在开发的特定应用程序及其预期环境。

现代版本的Python通过公开FileExistsError(在3.3 +中)来改进这段代码......

os.path.exists

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

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

答案 1 :(得分:1075)

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选项。 backport旨在提供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)

Per Bug 10948,这种替代方案的一个严重限制是,对于给定路径,每个python进程只能运行一次。换句话说,如果您使用它来创建目录,然后从Python内部或外部删除目录,然后再次使用mkpath重新创建同一目录,mkpath将只是默默地使用其无效的缓存信息先前已创建目录,并且实际上不会再次创建该目录。相反,os.makedirs不依赖于任何此类缓存。对于某些应用程序,此限制可能没问题。


关于目录的模式,如果您愿意,请参阅文档。

答案 2 :(得分:590)

使用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'并从中删除所有权限,则会OSError引发errno.EACCES(权限被拒绝,错误13)。

答案 3 :(得分:93)

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

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

答案 4 :(得分:68)

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

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

答案 5 :(得分:58)

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

答案 6 :(得分:38)

了解这种情况的细节

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

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

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

import os
filepath = '/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)

如果目录或文件不在那里,您将获得一个带有相关错误编号的IOErrorerrno.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)

答案 7 :(得分:31)

尝试os.path.exists功能

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

答案 8 :(得分:28)

我已经放下了以下内容。但这并非完全万无一失。

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

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

答案 9 :(得分:21)

  

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

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

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)
    User-callable function to create and return a unique temporary
    directory.  The return value is the pathname of the directory.

    The directory is readable, writable, and searchable only by the
    creating user.

    Caller is responsible for deleting the directory when done with it.

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

有一个新的Path对象(截至3.4),其中有许多方法需要与路径一起使用 - 其中一个是mkdir

(对于上下文,我用脚本跟踪我的每周代表。这里是脚本中代码的相关部分,允许我避免每天多次针对相同数据访问Stack Overflow 。)

首先是相关进口:

from pathlib import Path
import tempfile

我们现在不必处理os.path.join - 只需使用/加入路径部分:

directory = Path(tempfile.gettempdir()) / 'sodata'

然后我自觉地确保目录存在 - {3}}参数出现在Python 3.5中:

exist_ok

这是documentation的相关部分:

  

如果directory.mkdir(exist_ok=True) 为真,则忽略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)) 路径的其他API可以使用它们之前,必须将

Path个对象强制转换为str

也许应该更新Pandas以接受抽象基类的实例str

答案 10 :(得分:19)

在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.

答案 11 :(得分:12)

relevant Python documentation建议使用EAFP coding style (Easier to Ask for Forgiveness than Permission)。这意味着代码

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

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

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

答案 12 :(得分:9)

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

os.makedirs(path,exist_ok=True)

Python2 中,os.makedirs不支持设置exist_ok。您可以使用heikki-toivonen's answer中的方法:

import os
import errno

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

答案 13 :(得分:8)

您可以使用mkpath

# Create a directory and any missing ancestor directories. 
# If the directory already exists, do nothing.

from distutils.dir_util import mkpath
mkpath("test")    

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

适用于Python 2和3。

答案 14 :(得分:7)

对于单线程解决方案,您可以使用IPython.utils.path.ensure_dir_exists()

from IPython.utils.path import ensure_dir_exists
ensure_dir_exists(dir)

documentation确保目录存在。如果它不存在,尝试创建它并防止竞争条件,如果另一个进程正在做同样的事情。

答案 15 :(得分:7)

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

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

答案 16 :(得分:6)

您可以使用os.listdir

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

答案 17 :(得分:6)

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

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

答案 18 :(得分:6)

使用文件I / O时,需要考虑的重要事项是

TOCTTOU(检查时间到使用时间)

因此,使用{{1}}进行检查,然后稍后进行读取或写入可能会导致未处理的I / O异常。最好的方法是:

{{1}}

答案 19 :(得分:6)

我发现这个Q / A,我最初对一些失败和错误感到困惑。我正在使用Python 3(在Arch Linux x86_64系统上的Anaconda虚拟环境中的v.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 file
            with 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] How can I create a directory if it does not exist?

[2] https://docs.python.org/3/library/os.html#os.makedirs

答案 20 :(得分:4)

如果您考虑以下事项:

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

表示存在目录(路径)并且是目录。所以对我来说这样做是我需要的。所以我可以确保它是文件夹(不是文件)并且存在。

答案 21 :(得分:4)

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

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

应该在大多数系统上都能解决问题。

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

如果您需要错误处理:

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

答案 22 :(得分:3)

在程序/项目的入口点调用函数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')

答案 23 :(得分:2)

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

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

答案 24 :(得分:1)

import os
if os.path.isfile(filename):
    print "file exists"
else:
    "Your code here"
  

此处的代码使用(触摸)命令

这将检查文件是否存在,如果不存在,那么它将创建它。

答案 25 :(得分:0)

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

import os,sys,inspect
import 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)

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

答案 26 :(得分:0)

在Linux下,您可以在一行中创建目录:

import os
os.system("mkdir -p {0}".format('mydir'))

答案 27 :(得分:0)

您可以在1个命令中创建文件及其所有父目录,扩展名为fastcore到pathlib:path.mk_write(data)

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