首页 文章

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

提问于
浏览
3238

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

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

25 回答

  • 7

    使用try除了和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) .

  • 27

    Python 3.5:

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

    上面使用的pathlib.Path.mkdir以递归方式创建目录,如果目录已存在则不会引发异常 . 如果您不需要或不想创建父项,请跳过 parents 参数 .

    Python 3.2:

    Using pathlib:

    如果可以,请安装名为pathlib2的当前 pathlib backport . 不要安装名为pathlib的旧的非维护后端口 . 接下来,请参阅上面的Python 3.5部分并使用它 .

    如果使用Python 3.4,即使它带有 pathlib ,它也缺少有用的 exist_ok 选项 . backport旨在提供 mkdir 的更新且更优越的实现,其中包括此缺失选项 .

    Using 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:

    Using pathlib:

    如果可以,请安装名为pathlib2的当前 pathlib backport . 不要安装名为pathlib的旧的非维护后端口 . 接下来,请参阅上面的Python 3.5部分并使用它 .

    Using 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 不依赖于任何此类缓存 . 对于某些应用,此限制可能没问题 .


    关于目录的模式,如果您关心它,请参阅文档 .

  • 5

    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
    
  • 6

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

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

  • 584

    您可以使用 os.listdir

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

    我看到了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
    
  • 7

    在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.
    
  • 5
    import os
    if os.path.isfile(filename):
        print "file exists"
    else:
        "Your code here"
    

    这里的代码使用(touch)命令

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

  • 2

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

    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
    

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

  • 2

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

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

    了解这种情况的具体情况

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

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

    我们希望避免覆盖内置函数 dir . 另外, filepath 或者 fullfilepath 可能是一个比 filename 更好的语义名称,所以写得更好:

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

    你的最终目标是打开这个文件,你最初说,写,但你实际上是接近这个目标(基于你的代码),这样打开 reading 的文件:

    如果不是os.path.exists(目录):
    os.makedirs(目录)
    f =文件(文件名)

    假设开放阅读

    为什么要为您希望在那里并且能够阅读的文件创建一个目录?

    只是尝试打开文件 .

    with open(filepath) as my_file:
        do_stuff(my_file)
    

    如果目录或文件不是't there, you' ll得到带有相关错误编号的 IOErrorerrno.ENOENT 将指向正确的错误编号,无论您的平台如何 . 如果你愿意,你可以 grab 它,例如:

    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)
    
  • 33

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

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

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

    try:
        os.makedirs(dir_path)
    except OSError as e:
        if e.errno != errno.EEXIS:
            raise
    
  • 18

    我看到两个具有良好品质的答案,每个答案都有一个小缺陷,所以我会考虑它:

    尝试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 并检查嵌入的错误代码(请参阅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 ,但假设另一个在第一次检查后创建了目录,然后在第二次检查之前将其删除 - 我们仍然可能被愚弄 .

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

  • 90

    如果您考虑以下因素:

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

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

  • 24

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

  • 10

    如果在支持shell语言的机器上运行,为什么不使用子进程模块?适用于python 2.7和python 3.6

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

    应该在大多数系统上做到这一点 .

  • 6

    试试os.path.exists功能

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

    从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开始得到一个 exists_ok 标志) .

  • 5

    我发现了这个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

  • 0

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

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

    if not os.path.isdir(test_img_dir):
         os.mkdir(str("./"+test_img_dir))
    
  • 61

    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
    

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

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

  • -1

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

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

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

    or 如果使目录受到竞争条件的影响(即,如果在检查路径后存在,则可能还有其它东西)执行此操作:

    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中的新功能:带有exist_ok的pathlib.Path

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

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

    首先是相关进口:

    from pathlib import Path
    import tempfile
    

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

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

    然后我自觉地确保目录存在 - 在Python 3.5中显示 exist_ok 参数:

    directory.mkdir(exist_ok=True)
    

    这是documentation的相关部分:

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

    在期望 str 路径的其他API可以使用它们之前,必须将 Path 个对象强制转换为 str .

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

  • 940

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

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

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

  • 15

    你可以用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 .

相关问题