Python中的mkdir-p功能

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

535255 次浏览

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

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

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

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

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

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

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


对于Python≥3.2,os.makedirs有一个可选的第三个参数#1,当True时,启用mkdir -p功能-提供除非mode并且现有目录具有与预期权限不同的权限;在这种情况下,OSError如前所述被引发:

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

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

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

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

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

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

(更新)另见此非常相似的问题;我同意接受的答案(和警告),除了我会推荐os.path.isdir而不是os.path.exists

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

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

这比捕获异常更容易:

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

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

更新2012-07-27

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

在Python>=3.2中,这是

os.makedirs(path, exist_ok=True)

在早期版本中,使用@张子安的回答

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

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

代码在先,解释在后:

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

正如对@tzot回答的评论所表明的那样,在实际创建目录之前检查是否可以创建目录存在问题:您无法判断是否有人在此期间更改了文件系统。这也符合Python请求原谅而不是许可的风格。

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

正如雅各布·加布里尔森指出的那样,我们必须寻找的情况之一是我们试图放置目录的文件已经存在的情况。

使用mkdir -p

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

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

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

找出发生了什么的唯一方法是再次检查文件系统以查看那里是否有目录。如果有,然后静默返回,否则引发异常。

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

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

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

函数声明;

import osdef mkdir_p(filename):
try:folder=os.path.dirname(filename)if not os.path.exists(folder):os.makedirs(folder)return Trueexcept: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))

Pathlib来自python3标准库:

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

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

如果exist_ok为true,FileExistsError异常将被忽略行为为POSIX mkdir-p命令),但仅当最后一条路径组件不是现有的非目录文件。

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

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

def mkdirRecursive(dirpath):import osif os.path.isdir(dirpath): return
h,t = os.path.split(dirpath) # head/tailif not os.path.isdir(h):mkdirRecursive(h)
os.mkdir(join(h,t))# end mkdirRecursive
import osfrom os.path import join as join_paths
def mk_dir_recursive(dir_path):
if os.path.isdir(dir_path):returnh, t = os.path.split(dir_path)  # head/tailif 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的回答,但bug固定在树的一部分已经存在的地方