【问题标题】:mkdir -p functionality in Python [duplicate]Python中的mkdir -p功能[重复]
【发布时间】:2010-10-10 15:49:35
【问题描述】:

有没有办法从 Python 中在 shell 上获得类似于 mkdir -p 的功能。我正在寻找系统调用以外的解决方案。代码肯定不到20行,不知道有没有人写过?

【问题讨论】:

标签: python path directory-structure mkdir


【解决方案1】:

对于 Python ≥ 3.5,使用 pathlib.Path.mkdir:

import pathlib
pathlib.Path("/tmp/path/to/desired/directory").mkdir(parents=True, exist_ok=True)

exist_ok 参数是在 Python 3.5 中添加的。


对于 Python ≥ 3.2,os.makedirs 有一个 optional third argument exist_ok,当 True 启用 mkdir -p 功能时——除非提供了 mode 并且现有目录具有不同的权限比预期的;在这种情况下,OSError 会像以前一样引发:

import os
os.makedirs("/tmp/path/to/desired/directory", exist_ok=True)

对于更旧版本的 Python,您可以使用 os.makedirs 并忽略错误:

import errno    
import os

def mkdir_p(path):
    try:
        os.makedirs(path)
    except OSError as exc:  # Python ≥ 2.5
        if exc.errno == errno.EEXIST and os.path.isdir(path):
            pass
        # possibly handle other errno cases here, otherwise finally:
        else:
            raise

【讨论】:

  • 本着对很多人会复制+粘贴的东西进行微观改进的精神:如何将== 替换为!= 并删除pass/else :-)
  • 如果路径的最后一部分是文件,这似乎会失败,因为 exc.errno 等于 errno.EEXIST,所以一切看起来都很好,但实际上稍后使用该目录显然会失败。
  • distutils.dir_util.mkpath 怎么样?这很简单,就像mkpath('./foo/bar')
  • 优化异常处理?除了 OSError 作为 exc: if exc.errno != errno.EEXIST or not os.path.isdir(path): raise
  • @auraham,由于未记录的缓存,mkpath 有一些意外行为,如果您尝试像 mkdir -p:bugs.python.org/issue10948 一样使用它可能会导致问题。
【解决方案2】:

在 Python >=3.2 中,就是

os.makedirs(path, exist_ok=True)

在早期版本中,使用@tzot's answer

【讨论】:

    【解决方案3】:

    这比捕获异常更容易:

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

    免责声明 这种方法需要两个系统调用,这在某些环境/条件下更容易受到竞争条件的影响。如果您正在编写比在受控环境中运行的简单一次性脚本更复杂的东西,那么您最好使用只需要一个系统调用的公认答案。

    2012 年 7 月 27 日更新

    我很想删除这个答案,但我认为下面的评论线程很有价值。因此,我将其转换为 wiki。

    【讨论】:

    • 这样,在所有多任务操作系统中,makedirs 失败的可能性降低但并非不可能。这就像说“256 个字符对于创建的任何路径都足够了”。
    • @Asa 当然。 mkdir -p 也会抱怨这一点。我错过了你的意思吗?
    • @jholloway7:根据要求(“mkdir -p”-like 功能)Asa 的评论是不必要的。但是,我想知道您是否承认在调用 .exists 时目录可能不存在,而在调用 .makedirs 时目录可能存在。
    • @TZ 是的,我当然承认这一点。同样,如果没有原始海报的完整规范,我的假设是他/她想要一个可用于创建目录树的解决方案,如果不存在于简单脚本中,而不是具有 SLA 的 HA 企业生产解决方案。
    • @Asa 这就是例外的原因,出乎意料的出错了。如果您没有权限,则异常会一直冒泡,您会注意到要修复权限。应该是这样。
    【解决方案4】:

    最近发现了这个distutils.dir_util.mkpath

    In [17]: from distutils.dir_util import mkpath
    
    In [18]: mkpath('./foo/bar')
    Out[18]: ['foo', 'foo/bar']
    

    【讨论】:

    • 当心,mkpath() 会缓存目录,因此您无法重新 mkpath() 使用不同方法删除的目录:bugs.python.org/issue10948
    • @romanows 此外,该方法是私有的,以防其他人想阅读错误报告以查看它是否已“修复”(这不是错误)。
    • @MauroBaraldi 的重点是,如果您使用此方法创建一个目录,它会被删除,然后您尝试在同一程序中使用此方法再次创建它,它将无法正常工作。不要使用这个。
    【解决方案5】:

    如果文件已经存在,mkdir -p 会给你一个错误:

    $ touch /tmp/foo
    $ mkdir -p /tmp/foo
    mkdir: cannot create directory `/tmp/foo': File exists
    

    因此,如果os.path.isdir 返回False(检查errno.EEXIST 时),则对先前建议的改进是重新raise 异常。

    (更新)另见highly similar question;我同意接受的答案(和警告),但我会推荐 os.path.isdir 而不是 os.path.exists

    (更新)根据 cmets 中的建议,完整功能如下所示:

    import os
    def mkdirp(directory):
        if not os.path.isdir(directory):
            os.makedirs(directory) 
    

    【讨论】:

    • 你对这个案子是绝对正确的;但是,程序应该稍后捕获异常,例如当试图打开(“/tmp/foo/a_file”,“w”)时,我认为不需要更新。您可以改用 Python 代码更新您的答案,并观看它被投票;)
    • 在很多情况下可能没问题。不过,总的来说,我希望代码尽早失败,以便清楚真正导致问题的原因。
    • 如果它已经作为目录存在,mkdir -p 不会出错。如果您要求它创建一个目录,并且该名称的文件已经存在,它会出错。
    • @FrankKlotz 这就是为什么我调用 os.path.isdir 而不是 os.path.exists
    • -1 因为这个答案中唯一真正回答了问题的部分(最后一个代码块)回答错误并且还重复了其他答案。
    【解决方案6】:

    使用来自 python3 标准库的Pathlib

    Path(mypath).mkdir(parents=True, exist_ok=True)
    

    如果 parents 为真,则此路径的任何缺少的 parent 都将创建为 需要;它们是使用默认权限创建的,而不采用 考虑到模式(模仿 POSIX mkdir -p 命令)。 如果exist_ok 为false(默认值),则引发FileExistsError,如果 目标目录已经存在。

    如果exist_ok 为真,FileExistsError 异常将被忽略(同 POSIX mkdir -p 命令的行为),但前提是最后一个路径 组件不是现有的非目录文件。

    3.5 版更改: 添加了exist_ok 参数。

    【讨论】:

    • 对于python pathlib2。 pip install pathlib2; from pathlib2 import Path
    • 我不喜欢这种方法 - 更喜欢 os.mkdir 选项。如果您删除并重新创建与 shutil.rmtree 一起使用的文件夹,则静默继续 - 留下锁。 os 版本拒绝访问 - 并且提前结束而不是推迟结束。设置一个无法输出结果的巨大转换结果文件夹
    【解决方案7】:

    正如在其他解决方案中提到的,我们希望能够在模仿mkdir -p 的行为的同时访问文件系统一次。我认为这是不可能的,但我们应该尽可能接近。

    先代码,后解释:

    import os
    import errno
    
    def mkdir_p(path):
        """ 'mkdir -p' in Python """
        try:
            os.makedirs(path)
        except OSError as exc:  # Python >2.5
            if exc.errno == errno.EEXIST and os.path.isdir(path):
                pass
            else:
                raise
    

    正如@tzot 回答的cmets 表明在实际创建目录之前检查是否可以创建目录存在问题:您无法判断是否有人同时更改了文件系统。这也符合 Python 请求宽恕而不是许可的风格。

    所以我们应该做的第一件事是尝试创建目录,然后如果出错,找出原因。

    正如 Jacob Gabrielson 所指出的,我们必须寻找的一种情况是文件已经存在于我们试图放置目录的情况。

    mkdir -p:

    $ touch /tmp/foo
    $ mkdir -p /tmp/foo
    mkdir: cannot create directory '/tmp/foo': File exists
    

    Python 中的类似行为是引发异常。

    所以我们必须弄清楚是否是这种情况。不幸的是,我们不能。无论目录存在(好)还是存在阻止创建目录的文件(坏),我们都会从 makedirs 得到相同的错误消息。

    解决问题的唯一方法是再次检查文件系统,看看那里是否有目录。如果有,则静默返回,否则引发异常。

    唯一的问题是文件系统现在可能处于与调用 makedirs 时不同的状态。例如:一个文件存在导致 makedirs 失败,但现在一个目录在它的位置。这并不重要,因为该函数只会在最后一次文件系统调用时目录存在时静默退出而不会引发异常。

    【讨论】:

    • 或者只是:os.makedirs(path, exist_ok=True)
    【解决方案8】:

    我认为 Asa 的回答基本上是正确的,但您可以将其扩展一点,使其更像mkdir -p,或者:

    import os
    
    def mkdir_path(path):
        if not os.access(path, os.F_OK):
            os.mkdirs(path)
    

    import os
    import errno
    
    def mkdir_path(path):
        try:
            os.mkdirs(path)
        except os.error, e:
            if e.errno != errno.EEXIST:
                raise
    

    这些都处理路径已经静默存在但让其他错误冒泡的情况。

    【讨论】:

    • Python 2.7.6 ... [GCC 4.8.2] on linux2上,至少看起来应该是os.mkdir,而不是os.mkdirs
    • 第一个选项容易受到竞争条件的影响(在某一时刻,目录不存在,所以我们继续创建它,但在中间有其他东西创建它并繁荣!)第二个选项是方式进入 Python 2
    【解决方案9】:

    函数声明;

    import os
    def mkdir_p(filename):
    
        try:
            folder=os.path.dirname(filename)  
            if not os.path.exists(folder):  
                os.makedirs(folder)
            return True
        except:
            return False
    

    用法:

    filename = "./download/80c16ee665c8/upload/backup/mysql/2014-12-22/adclient_sql_2014-12-22-13-38.sql.gz"
    
    if (mkdir_p(filename):
        print "Created dir :%s" % (os.path.dirname(filename))
    

    【讨论】:

      【解决方案10】:
      import os
      import tempfile
      
      path = tempfile.mktemp(dir=path)
      os.makedirs(path)
      os.rmdir(path)
      

      【讨论】:

        【解决方案11】:

        我个人在以下方面取得了成功,但我的函数可能应该被称为“确保此目录存在”:

        def mkdirRecursive(dirpath):
            import os
            if os.path.isdir(dirpath): return
        
            h,t = os.path.split(dirpath) # head/tail
            if not os.path.isdir(h):
                mkdirRecursive(h)
        
            os.mkdir(join(h,t))
        # end mkdirRecursive
        

        【讨论】:

        • 这是 2.7 的一个不错的答案,似乎比捕获错误更干净
        • 如果树的一部分已经存在则失败,所以这里有一个修复:- import os; from os.path import join as join_paths def mk_dir_recursive(dir_path): if os.path.isdir(dir_path): return h, t = os.path.split(dir_path) # head/tail if not os.path.isdir(h): mk_dir_recursive(h) new_path = join_paths(h, t) if not os.path.isdir(new_path): os.mkdir(new_path)
        【解决方案12】:
        import os
        from os.path import join as join_paths
        
        def mk_dir_recursive(dir_path):
        
            if os.path.isdir(dir_path):
                return
            h, t = os.path.split(dir_path)  # head/tail
            if not os.path.isdir(h):
                mk_dir_recursive(h)
        
            new_path = join_paths(h, t)
            if not os.path.isdir(new_path):
                os.mkdir(new_path)
        

        基于@Dave C 的回答,但修复了部分树已经存在的错误

        【讨论】:

          猜你喜欢
          • 2012-05-04
          • 2014-01-27
          • 2014-05-09
          • 1970-01-01
          • 1970-01-01
          • 2014-06-21
          • 2017-06-02
          相关资源
          最近更新 更多