ChatGPT解决这个技术问题 Extra ChatGPT

Python中的mkdir -p功能[重复]

这个问题在这里已经有了答案:如何安全地创建嵌套目录? (29 个回答) 5 年前关闭。

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

如何在 os.command 中实现“mkdir -p /home/Documents/Folder/{Subfolder1,Subfolder2}”等效?它创建一个文件夹 {Subfolder1,Subfolder2} 而不是 2 个不同的文件夹

M
Mateen Ulhaq

对于 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 一样使用它,可能会导致问题。
C
Community

在 Python >=3.2 中,这是

os.makedirs(path, exist_ok=True)

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


这对我有用。要了解原因,请参阅此处的文档(搜索“makedirs”):docs.python.org/3/library/os.html
5
5 revs, 2 users 97%

这比捕获异常更容易:

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

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

更新 2012-07-27

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


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

最近,我发现了这个 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 的重点是,如果您使用此方法创建一个目录,它会被删除,然后您尝试在同一程序中使用此方法再次创建它,它将无法正常工作。不要使用这个。
u
udondan

如果文件已经存在,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

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

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,因为该答案中唯一真正回答问题的部分(最后一个代码块)回答错误并且还重复了其他答案。
A
Antony Hatchkins

使用 python3 标准库中的 Pathlib

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

如果 parents 为真,则根据需要创建此路径的任何缺少的 parent;它们是使用默认权限创建的,不考虑模式(模仿 POSIX mkdir -p 命令)。如果 exists_ok 为 false(默认值),如果目标目录已存在,则会引发 FileExistsError。如果exist_ok 为真,FileExistsError 异常将被忽略(与 POSIX mkdir -p 命令的行为相同),但前提是最后一个路径组件不是现有的非目录文件。在 3.5 版更改: 添加了 exists_ok 参数。


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

正如在其他解决方案中提到的,我们希望能够在模仿 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 失败,但现在一个目录在它的位置。这并不重要,因为该函数只会在最后一次文件系统调用时目录存在时静默退出而不会引发异常。


或者只是:os.makedirs(path, exist_ok=True)
d
davidavr

我认为 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 的方式
G
Guray Celik

函数声明;

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

p
pixelbeat
import os
import tempfile

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

D
Dave C

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

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)
D
Dave00Galloway
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's answer但修复了部分树已经存在的错误