如何删除使用tempfile.mkdtemp创建的目录?

66 投票
5 回答
65324 浏览
提问于 2025-04-16 22:35

我有一个Python程序,它通过使用tempfile.mkdtemp/temp下创建临时目录。不幸的是,这个Python程序在使用完这些目录后没有删除它们。所以现在磁盘空间不够用了。

问题:

  1. 我该如何手动删除/temp下留下的临时目录?我尝试手动删除,但出现了“权限被拒绝”的错误。
  2. 在Python程序中,如何在使用完temp目录后将其删除?

5 个回答

5

我遇到过类似的问题,使用TemporaryDirectory()这个功能,基本上就是你上面提到的功能。

我的问题出在临时目录的使用上。我是通过克隆一个git仓库来填充内容的,结果在这个过程中生成了一些只读文件。正常退出时,这些只读的临时文件导致整个临时目录没有被删除。

我把TemporaryDirectory继承到我自己的类里,并重写了_cleanup这个类方法,代码如下。

在super()之前的代码可能可以优化,但对我来说,性能不是问题。

我确实用了强制的方法,查看了"tempfile"的源代码。

import tempfile
import shutil
import stat 

class myTempDir(tempfile.TemporaryDirectory):
    @classmethod
    def _cleanup(self,name, warn_message):
        for root, dirs, files in os.walk(name):
            for fname in files:
                full_path = os.path.join(root, fname)
                os.chmod(full_path ,stat.S_IWRITE)
        super()

这个解决方案在Windows 10和Python 3上有效。

36

看看这个文档,很简单哦。;) 文档里说,这个临时目录只有创建它的用户可以读取、写入和搜索。

如果你想删除这个临时目录,可以试试下面的代码:

import errno
import shutil
import tempfile

try:
    tmp_dir = tempfile.mkdtemp()  # create dir
    # ... do something
finally:
    try:
        shutil.rmtree(tmp_dir)  # delete directory
    except OSError as exc:
        if exc.errno != errno.ENOENT:  # ENOENT - no such file or directory
            raise  # re-raise exception

你也可以试试tempdir这个包,或者看看它的源代码。

96

在Python中管理资源(比如文件)时,最好的做法是使用with这个关键词,它可以自动释放资源(也就是清理,比如关闭文件);这个功能从Python 2.5开始就有了。

从Python 3.2开始,你可以使用tempfile.TemporaryDirectory()来代替tempfile.mkdtmp(),这个方法可以和with一起使用,并且会自动清理目录:

from tempfile import TemporaryDirectory

with TemporaryDirectory() as temp_dir:
    # ... do something with temp_dir
# automatically cleaned up when context exited

如果你使用的是早期版本的Python(至少是2.5,所以可以用with),你可以使用backports.tempfile;可以参考Nicholas Bishop的回答,了解Python 2.7中的tempfile.TemporaryDirectory上下文管理器

你可以轻松地自己写一个类,叫做上下文管理器__enter__()方法的返回值会绑定到as后面的目标,而__exit__()方法会在退出上下文时被调用——即使是因为异常退出——并进行清理。

import shutil
import tempfile

class TemporaryDirectory(object):
    """Context manager for tempfile.mkdtemp() so it's usable with "with" statement."""
    def __enter__(self):
        self.name = tempfile.mkdtemp()
        return self.name

    def __exit__(self, exc_type, exc_value, traceback):
        shutil.rmtree(self.name)

你可以使用@contextlib.contextmanager这个装饰器来简化这个过程,这样你就不需要手动编写上下文管理器。yield之前的代码在进入上下文时执行,yield的值会绑定到as后面的目标,而yield之后的代码在退出上下文时执行。这本质上是一个协程,它封装了资源的获取和释放,yield将控制权交给with语句的主体。需要注意的是,这里你确实需要有一个try...finally块,因为@contextlib.contextmanager不会捕获yield中的异常——这只是将资源管理的过程转化为一个协程。

from contextlib import contextmanager
import tempfile
import shutil

@contextmanager
def TemporaryDirectory():
    name = tempfile.mkdtemp()
    try:
        yield name
    finally:
        shutil.rmtree(name)

正如simplylizz所提到的,如果你不介意目录已经被删除(上面的代码假设不会发生这种情况),你可以像下面这样捕获“No such file or directory”的异常:

import errno
# ...
try:
    shutil.rmtree(self.name)
except OSError as e:
    # Reraise unless ENOENT: No such file or directory
    # (ok if directory has already been deleted)
    if e.errno != errno.ENOENT:
        raise

你可以和标准实现进行比较,标准实现可以在tempfile.py中找到;即使是这个简单的类,多年来也出现过bug并不断演变。

关于with的背景,可以参考:

撰写回答