首页 > 解决方案 > 您应该关闭临时创建的文件还是可以直接删除它们?

问题描述

如果您有一个文件是在单个函数的范围内出于某种目的临时创建的,并且在函数结束时将其删除延迟,您是否应该在删除临时文件之前关闭临时文件,或者确实os.Remove()释放所有文件?相关资源?

func foo() error {
    tempFile, err := os.Create(filePath)
    if err != nil {
        return err
    }
    defer func() {
        /* Is this necessary?
            if err = tempFile.Close(); err != nil {
                log.Error(err)
                return
            }
        */

        if err = os.Remove(filePath); err != nil {
            log.Error(err)
        }
    }()

    // do something

    return nil
}

标签: filego

解决方案


您是否应该在删除临时文件之前将其关闭或是否os.Remove()会处理?

“这取决于”。

首先,os.Remove()是文件系统级别的操作;它会修改文件系统上的目录条目,并且不知道要删除的文件在调用它的进程中可能有多少打开的文件描述符。
是的,您可以多次打开同一个文件 - 这仅由系统强加的限制。

还要考虑在相当现代的文件系统上存在“链接”的概念(NTFS 可能会以不同的方式称呼它们,上次我检查它们有几个概念,用于在类 Unix 上称为“指向目录的符号链接”系统),因此,您可能会通过不同的路径名在物理上打开相同的文件,并对os.Remove路径名进行操作。

再加上文件系统是一个活动对象这一事实:在文件系统上的某个路径名打开文件到您即将使用该路径名删除文件的时间之间,该路径名甚至可能不复存在(但请继续阅读)。

这应该向您保证,不,os.Remove不会尝试查找和关闭所有os.File已知引用同一文件的实例。

其次,类 Unix 系统和 Windows 在处理打开文件的行为上存在巨大差异;符合POSIX标准的文件系统需要将访问特定文件系统的系统中任何进程中打开的文件视为对该文件的实时引用。
从文档中可能不会立即清楚这一点,但这在close(2)系统调用参考手册中有记录:

如果文件的链接计数为0,则当与该文件关联的所有文件描述符都关闭时,该文件占用的空间将被释放,该文件将不再可访问。

基本上这意味着即使引用文件系统上特定文件的所有路径名都被删除(因此文件被文件系统呈现为不可访问),它的数据仍然在文件系统上,直到打开到该文件的最后一个文件描述符在运行中关闭过程。

这种“技巧”经常用于 Unix-only 软件来存储临时数据:

  1. 创建一个临时文件(通常使用mktemp(2); 在 Go 中是io/ioutil.TempFile)。
  2. 然后立即在文件系统上删除它(同时保持打开状态)。
  3. 然后通过文件描述符使用它任意时间。
  4. 当描述符关闭时,文件系统最终回收文件数据占用的空间。

这在 Windows 上不起作用:在那里,无法删除打开的文件,在某些情况下甚至无法重命名它,因此上面的“技巧”不起作用。

所以,让我们用新知识重新考虑原来的问题。

  • 最安全的选择可能是编写一个简单的帮助程序来清理临时文件,它看起来像

    func removeTempFile(fd *os.File) {
      for _, err := range [...]error{fd.Close(), os.Remove(fd.Name())} {
        if err != nil {
          log.Print("failed to remove temp file: ", err)
          // Note that we merely log errors, not blow up on them -
          // this is to report both errors if all operations have failed.
        }
      }
    }
    

    然后defer removeTempFile(fd) 在打开每个新的临时文件后粘贴。

  • 如果您确定您的程序只能在与 Unix 兼容的系统(基于 Linux 和 *BSD、Mac OS 等)上运行,您可以立即删除临时文件并仅推迟关闭它。

    请注意,如果您确实需要通过文件名引用该文件,这显然是行不通的——例如,您将其传递给正在执行的某个外部程序。


推荐阅读