有没有办法将Ruby临时文件变成永久文件?

32
如果我通过 Tempfile 创建了一个临时文件,有没有什么方法可以使它变为“永久”的,而无需将其复制到另一个文件中,以避免在关联的 Tempfile 实例被垃圾收集或进程终止时被删除?
另外,是否有一种方法可以利用 Tempfile 机制(或使用类似的机制)来获得“新”的文件名,而无需在该名称下创建文件?

只是为了明确:你的意思是你仍然需要一个临时文件,这个文件在进程成功退出后最终被销毁吗? - Patrick Oscity
@atmosx 通过“permanent”,我指的是“文件系统中的文件,在垃圾回收或退出时不会被删除”。顺便说一下,我对Ruby Tempfile机制的理解是它实际上是在文件系统中创建文件,而不仅仅是在内存中,唯一“临时”的事情是它们会自动删除。事实上,我已经能够从其中一个这些Tempfile对象(例如,在调试器中)获取路径名,并在另一个终端窗口的shell命令中将其作为文件访问。 - Peter Alfvin
@p11y 不,我希望这个临时文件不再是临时的(即不被删除)。 - Peter Alfvin
3
和我的大多数Stack Overflow的问题一样,我对答案的兴趣不仅限于特定的问题或用例。但在这个特定的情况下,我的应用程序中使用了许多临时文件,有时在调试或开发过程中,我想要轻松地在程序退出后“保留”其中一些以供分析。 - Peter Alfvin
如果您不希望它们是临时的,那么您必须接管它们的创建和删除。Ruby的TempFile会在操作系统指定的临时文件位置创建文件,因此即使Ruby没有机会删除它,一旦系统运行清理任务,它也会被删除。 - the Tin Man
显示剩余2条评论
2个回答

27

不是很确定。关于这个问题本身,请看这里:

ObjectSpace.undefine_finalizer(tmpfile)

Tempfile库使用Ruby ObjectSpace终结器在垃圾回收时自动删除自身。如果您不删除它,可以使用上述行来删除Tempfile的自删除功能。例如:

$ irb
2.0.0p0 :001 > require "tempfile"
 => true 
2.0.0p0 :002 > t = Tempfile.new("test")
 => #<Tempfile:/tmp/test20140122-6655-80p4b7> 
2.0.0p0 :003 > t.write("Hi!")
 => 3 
2.0.0p0 :004 > ObjectSpace.undefine_finalizer(t)
 => #<Tempfile:/tmp/test20140122-6655-80p4b7> 
2.0.0p0 :005 > exit
$ cat /tmp/test20140122-6655-80p4b7
Hi!
$ 

然而还有一些需要注意的事项。Tempfile将使用系统临时文件目录,例如 /tmp,这是操作系统定期自动清除的(例如,每次开机)。因此,即使您“持久化”了该文件,您也需要接受它可能会消失的可能性,或者将其移动到默认不被清除的目录中,例如/var/tmp(用于存储临时文件的Linux目录)。


至于您的第二个问题,请尝试使用这里的代码:

Dir::Tmpname.create('your_application_prefix') { |path| puts path }

它需要一个require "tmpdir"

@PeterAlfvin:很高兴它们有帮助。 - Linuxios
最好的解决方案是不要使用TempFile来处理需要永久保存的文件,而是显式地创建/删除它们。TempFile适用于真正临时且可以轻松重新创建的文件。 - the Tin Man
@theTinMan 最适合谁?请看我在以“与我大多数SO问题一样…”开头的那个问题下的评论。 - Peter Alfvin
1
最适合那些使用TempFile创建文件,然后想要在事后保留它的人。相反,设置一个调试标志,使用普通的File类,这样你就有了持久性,当该标志未设置时使用TempFile。然后你就不必去处理类本身。至少你可以使用 $DEBUG,当你在命令行上传递 -d 时它会被设置。 - the Tin Man
@peter:我不知道。抱歉。也许可以尝试时间、PID和基本名称的组合? - Linuxios
显示剩余2条评论

12

我认为最简单的解决方案可能是对 Tmpfile 类进行猴子补丁,添加一个 persist 方法。这个方法会接受一个文件名作为参数,用于指定临时文件将要移动到的位置。此外,它还会删除终结器,以便在退出时不会删除临时文件。

require 'tempfile'
require 'fileutils'

class Tempfile
  def persist(filename)
    FileUtils.mv(self.path, filename)
    ObjectSpace.undefine_finalizer(self)
  end
end

file = Tempfile.new('tmp')
file.write('hello world')
file.close
file.persist('hello.txt')

运行这个程序将会创建一个持久文件./hello.txt,它是通过移动原始临时文件而不是复制它来完成的。

4
你能否只复制文件而不弄乱finalizer呢?我在想。 - Sergio Tulentsev
3
@SergioTulentsev,那也是我的最初想法,但问题明确要求除了复制以外的方式。 - Patrick Oscity

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接