Python中的mkdir -p功能

1027

有没有一种方法可以在Python中获得类似于shell中mkdir -p的功能?我想要的是除系统调用之外的解决方案。我相信代码不到20行,想知道是否有人已经写过这个功能?


如何在 os.command 中实现 "mkdir -p /home/Documents/Folder/{Subfolder1,Subfolder2}" 的等效操作?它会创建一个名为 {Subfolder1,Subfolder2} 的文件夹,而不是两个不同的文件夹。 - Akash Tyagi
os中的makedirs和mkdir有什么不同? - caot
"{Subfolder1,Subfolder2}" 语法是 Bash(和其他一些 shell)的一个特性,而不是 mkdir -p。例如,echo prefix-{Subfolder1,Subfolder2} 将显示 "prefix-Subfolder1 prefix-Subfolder2"。 - jejese
12个回答

1410

对于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 函数有一个可选的第三个参数 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

20
为了微调大量人会复制粘贴的内容,建议用!=代替==并删除pass/else :-) - Will Hardy
7
如果路径的最后一部分是文件,那么似乎会出现错误,因为exc.errno等于errno.EEXIST,所以一切似乎都正常,但实际上稍后使用该目录时将显然失败。 - elhefe
26
那么 distutils.dir_util.mkpath 呢?它非常简单,只需 mkpath('./foo/bar') 即可。 - auraham
4
优化异常处理? 除非OSError异常的错误代码是errno.EEXIST并且路径不是一个目录,否则会引发异常。 - Julian
11
@auraham,mkpath存在一些意外行为,由于未记录的缓存机制可能会导致问题,如果您尝试像使用mkdir -p一样使用它,则需要注意:http://bugs.python.org/issue10948。 - romanows
显示剩余14条评论

341

在 Python >=3.2 中,这样做:

os.makedirs(path, exist_ok=True)

在较早的版本中,请使用@tzot的答案


这对我有用。要了解原因,请参见此处的文档(搜索“makedirs”):https://docs.python.org/3/library/os.html - brethvoice

163

这比捕获异常更容易:

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

免责声明:这种方法需要两个系统调用,更容易在某些环境/条件下出现竞争条件。如果你要编写的程序比在受控环境中运行的简单临时脚本更为复杂,那么最好采用只需要一个系统调用的被接受答案。

更新于2012-07-27

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


28
这样做可以减少在所有多任务操作系统中makedirs失败的概率,但并不是完全不可能。这就像是说“256个字符应该足够创建任何路径”。 - tzot
4
当然可以。而且,mkdir -p也会抱怨这个问题。我有遗漏你的观点吗? - Joe Holloway
4
根据要求(类似于“mkdir -p”功能),Asa的评论是不必要的。然而,我想知道你是否承认当调用.exists时目录可能不存在,但在调用.makedirs时存在的可能性。 - tzot
6
是的,我完全认同。再次强调,如果原始帖子没有提供完整的规范说明,我的假设是,他/她想要一个简单的脚本解决方案来创建目录树(如果不存在),而不是一个带有SLAs的HA企业级生产解决方案。 - Joe Holloway
6
@Asa 这就是异常处理的作用,表示出现了意料之外的错误。如果您没有权限,则异常会一直向上冒泡,以提醒您修复权限问题。这正是应该发生的。 - jpsimons
显示剩余5条评论

48

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

In [17]: from distutils.dir_util import mkpath

In [18]: mkpath('./foo/bar')
Out[18]: ['foo', 'foo/bar']

13
注意,mkpath()会缓存目录,因此无法使用不同的方法重新创建已被删除的目录,请参考http://bugs.python.org/issue10948。 - romanows
8
此方法旨在保密,以防其他人想要阅读错误报告以查看是否已经“修复”(它并不是一个错误)。 - jtpereyda
@MauroBaraldi,问题在于如果你使用这种方法创建一个目录,然后它被删除了,你再次尝试使用同样的程序使用这种方法创建它,它将无法工作。不要使用这个方法。 - user3064538

17

mkdir -p如果文件已经存在会报错:

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

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

(更新)还可以参见这个非常相似的问题;我赞同被接受的答案(以及注意事项),不过我推荐使用os.path.isdir而不是os.path.exists

(更新)根据评论中的建议,完整的函数应该如下所示:

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

你在这个案例中是完全正确的;然而,程序应该稍后捕获异常,比如当尝试打开("/tmp/foo/a_file", "w")时,因此我认为不需要更新。你可以用Python代码更新你的答案,看它会被点赞 ;) - tzot
在很多情况下,这可能是可以接受的。然而,总的来说,我更喜欢代码尽早失败,这样就清楚了问题的真正原因。 - Jacob Gabrielson
11
如果目录已经存在,则 mkdir -p 不会报错。但是,如果要创建的目录名称已经被一个文件占用,则该命令会报错。请注意,这里不提供任何解释或其他信息。 - Frank Klotz
@FrankKlotz 这就是为什么我调用的是 os.path.isdir 而不是 os.path.exists。 - Jacob Gabrielson
-1 是因为这个回答中唯一实际回答问题的部分(最后的代码块)回答错误,并且重复了其他回答。 - wchargin
如果您使用pathlib,那么Path("/tmp/my/file").mkdir(parents=True, exist_ok=True)也会表现出相同的行为。它将引发一个FileExistsError异常。 - user3064538

17

使用Python3标准库中的Pathlib

Path(mypath).mkdir(parents=True, exist_ok=True)
如果 parents 为 True,该路径中缺失的任何父级目录都将根据需要创建;它们将使用默认权限创建,而不考虑模式(效仿 POSIX 的 mkdir -p 命令)。如果 exist_ok 为 False(默认值),则如果目标目录已存在,则会引发 FileExistsError 异常。
如果 exist_ok 为 True,则会忽略 FileExistsError 异常(与 POSIX 的 mkdir -p 命令相同的行为),但前提是最后一个路径组件不是现有的非目录文件。 自版本3.5开始更改:添加了 exist_ok 参数。

3
对于 Python 版本小于 3.5,可以使用 pathlib2。请执行以下命令安装:pip install pathlib2,然后在代码中添加导入语句:from pathlib2 import Path - Eyal Levin
我不喜欢这种方法 - 更喜欢使用os.mkdir选项。如果您在shutil.rmtree中删除并重新创建文件夹,则可以保持静默继续 - 避免锁定。使用os版本会出现访问被拒绝的情况,并且会早期崩溃。建立一个结果文件夹,以存储大量转换的结果,否则可能无法输出结果。 - JGFMK

14

正如其他解决方案中提到的那样,我们希望在模拟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答案下的评论所示,在实际创建目录之前检查是否可以创建目录存在问题:你无法判断其中是否有人在此期间更改了文件系统。这也符合Python请求宽恕而不是事先征得许可的风格。

因此,我们首先应该尝试创建目录,然后如果出现错误,再找出原因。

正如Jacob Gabrielson所指出的那样,我们必须注意一个情况,即我们要创建的目录位置已经存在一个文件。

使用mkdir -p

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

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

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

唯一确定发生了什么的方法是再次检查文件系统以查看是否存在该目录。如果有,则静默返回,否则引发异常。

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


2
os.makedirs(path, exist_ok=True) - Erik Aronesty

9

我认为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 - tsbertalan
第一种选项容易出现竞态条件(在某个瞬间,目录不存在,所以我们继续创建它,但是在中途,其他东西创建了它,然后就出问题了!)第二种选项是Python 2的正确选择。 - Clint Eastwood

5

函数声明;

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

2
import os
import tempfile

path = tempfile.mktemp(dir=path)
os.makedirs(path)
os.rmdir(path)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接