file - 您应该关闭临时创建的文件还是可以直接删除它们?
问题描述
如果您有一个文件是在单个函数的范围内出于某种目的临时创建的,并且在函数结束时将其删除延迟,您是否应该在删除临时文件之前关闭临时文件,或者确实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
}
解决方案
您是否应该在删除临时文件之前将其关闭或是否
os.Remove()
会处理?
“这取决于”。
首先,os.Remove()
是文件系统级别的操作;它会修改文件系统上的目录条目,并且不知道要删除的文件在调用它的进程中可能有多少打开的文件描述符。
是的,您可以多次打开同一个文件 - 这仅由系统强加的限制。
还要考虑在相当现代的文件系统上存在“链接”的概念(NTFS 可能会以不同的方式称呼它们,上次我检查它们有几个概念,用于在类 Unix 上称为“指向目录的符号链接”系统),因此,您可能会通过不同的路径名在物理上打开相同的文件,并对os.Remove
路径名进行操作。
再加上文件系统是一个活动对象这一事实:在文件系统上的某个路径名打开文件到您即将使用该路径名删除文件的时间之间,该路径名甚至可能不复存在(但请继续阅读)。
这应该向您保证,不,os.Remove
不会尝试查找和关闭所有os.File
已知引用同一文件的实例。
其次,类 Unix 系统和 Windows 在处理打开文件的行为上存在巨大差异;符合POSIX标准的文件系统需要将访问特定文件系统的系统中任何进程中打开的文件视为对该文件的实时引用。
从文档中可能不会立即清楚这一点,但这在close(2)
系统调用参考手册中有记录:
如果文件的链接计数为0,则当与该文件关联的所有文件描述符都关闭时,该文件占用的空间将被释放,该文件将不再可访问。
基本上这意味着即使引用文件系统上特定文件的所有路径名都被删除(因此文件被文件系统呈现为不可访问),它的数据仍然在文件系统上,直到打开到该文件的最后一个文件描述符在运行中关闭过程。
这种“技巧”经常用于 Unix-only 软件来存储临时数据:
- 创建一个临时文件(通常使用
mktemp(2)
; 在 Go 中是io/ioutil.TempFile
)。 - 然后立即在文件系统上删除它(同时保持打开状态)。
- 然后通过文件描述符使用它任意时间。
- 当描述符关闭时,文件系统最终回收文件数据占用的空间。
这在 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 等)上运行,您可以立即删除临时文件并仅推迟关闭它。
请注意,如果您确实需要通过文件名引用该文件,这显然是行不通的——例如,您将其传递给正在执行的某个外部程序。
推荐阅读
- javascript - 我需要制作一个 JavaScript 函数,当用户单击“搜索”按钮时将调用该函数
- rest - 在 REST API 中执行 SalesOrderAddInvoice 操作
- javascript - 调用这个和函数参数
- android - Android改造只显示部分结果
- r - 取列名,拆分它们并将它们融合到数据框中
- c++ - 如何在 Visual Studio 2019 C++ 中修复未解决的异常
- javascript - 文件选择时将文本移动到按钮的右侧
- javascript - 我可以向 vuetify 日历事件添加更多字段吗?
- java - 所需数据类型错误
- git - 将带有子模块的 Git 存储库从 GitHub 分叉到 GitLab