Python中的mkdir -p的功能

mkdir -p functionality in Python

本问题已经有最佳答案,请猛点这里访问。

有没有一种方法可以从python内部在shell上获得类似于mkdir -p的功能?我正在寻找系统调用以外的解决方案。我确信代码少于20行,我想知道是否有人已经写了它?


mkdir -p功能如下:

1
2
3
4
5
6
7
8
9
10
11
12
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
        else:
            raise

更新

对于python≥3.2,os.makedirs有一个可选的第三个参数exist_ok,如果为真,则启用mkdir -p功能,除非提供mode并且现有目录的权限与预期目录不同;在这种情况下,OSError会像以前一样被提升。

更新2

对于python≥3.5,也有pathlib.Path.mkdir

1
2
3
import pathlib

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

在python 3.5中添加了exist_ok参数。


在python中>=3.2,这是

1
os.makedirs(path, exist_ok=True)

在早期版本中,使用@tzot的答案。


这比捕获异常更容易:

1
2
3
import os
if not os.path.exists(...):
    os.makedirs(...)

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

更新2012-07-27

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


最近,我发现了distutils.dir_util.mkpath:

1
2
3
4
In [17]: from distutils.dir_util import mkpath

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


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

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

因此,如果os.path.isdir返回False(在检查errno.EEXIST时),那么对先前建议的改进将是重新raise例外。

(更新)另见这一高度相似的问题;我同意接受的回答(和注意事项),除非我建议使用os.path.isdir而不是os.path.exists

(更新)根据注释中的建议,完整功能如下:

1
2
3
4
import os
def mkdirp(directory):
    if not os.path.isdir(directory):
        os.makedirs(directory)


使用python3标准库中的pathlib:

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

If parents is true, any missing parents of this path are created as
needed; they are created with the default permissions without taking
mode into account (mimicking the POSIX mkdir -p command).
If exist_ok is false (the default), an FileExistsError is raised if
the target directory already exists.

If exist_ok is true, FileExistsError exceptions will be ignored (same
behavior as the POSIX mkdir -p command), but only if the last path
component is not an existing non-directory file.

Changed in version 3.5: The exist_ok parameter was added.


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

先编码,后解释:

1
2
3
4
5
6
7
8
9
10
11
12
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要求宽恕而不是允许的风格。

所以我们应该做的第一件事就是建立一个目录,如果它出错了,找出原因。

正如JacobGabrielson指出的那样,我们必须寻找的一个案例是,在我们试图放置目录的地方已经存在一个文件。

使用mkdir -p时:

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

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

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

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

唯一的问题是文件系统现在的状态可能与调用makedirs时的状态不同。一个文件存在,导致makedirs失败,但现在有一个目录在它的位置。这其实没什么关系,因为在最后一次文件系统调用目录时,函数只会安静地退出,而不会引发异常。


我认为阿萨的回答基本上是正确的,但你可以把它扩大一点,使其更像以东十一〔0〕号,或者:

1
2
3
4
5
import os

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

1
2
3
4
5
6
7
8
9
import os
import errno

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

这两种方法都可以处理路径已经静默存在的情况,但会让其他错误冒泡。


功能声明;

1
2
3
4
5
6
7
8
9
10
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

用途:

1
2
3
4
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))

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

1
2
3
4
5
6
7
8
9
10
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


1
2
3
4
5
6
import os
import tempfile

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
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的答案,但修复了树的一部分已经存在的错误