32

If I've created a temporary file through Tempfile, is there any way aside from copying it to another file that I can make it "permanent"? I'd like to avoid it getting deleted when the associated Tempfile instance is garbage collected or the process is terminated.

On a related point, is there a way to leverage the Tempfile mechanism (or use a similar mechanism) to get a "new" filename without having to create a file at that name?

Peter Alfvin
  • 28,599
  • 8
  • 68
  • 106
  • You have to define *permanent* in that context, because anything sitting on your RAM is, by definition, **note permanent**. So either you have to use file (database, txt, XML, etc.) or the ram. There's no way in between (technically there is, see [cold boot attacks](http://en.wikipedia.org/wiki/Cold_boot_attack) but that's a side-effect) that I can think of. But, out of curiosity, what are you trying to accomplish that neither RAM or the Filesystem are not good enough to handle? – patm Jan 22 '14 at 14:25
  • Just to be clear: you mean that you still want a temporary file which is finally disposed when the process exits successfully? – Patrick Oscity Jan 22 '14 at 14:35
  • @atmosx By "permanent", I mean "a file in the file system that won't be deletd on garbage collection or exit". FWIW, my understanding of the Ruby `Tempfile` mechanism is that it is actually creating files in the file system, not just in-memory and that the only thing "temporary" about them is that they get deleted automatically. Indeed, I've been able to take a path name from one of these `Tempfile` objects (e.g. while in the debugger) and access it as a file from a shell command in another terminal window. – Peter Alfvin Jan 22 '14 at 14:36
  • @p11y No, I want the temporary file to be no longer temporary (i.e. not deleted). – Peter Alfvin Jan 22 '14 at 14:37
  • 3
    @atmosx Like most of my SO questions, my interest in an answer extends beyond a particular problem or use case, but in this particular instance, I use many temporary files in my application and sometimes during debugging or development, I'd like to easily "preserve" some for analysis after the program exits. – Peter Alfvin Jan 22 '14 at 14:39
  • If you don't want them to be temporary, you have to take over their creation and deletion. Ruby's TempFile creates the file in a place designated by the OS for temporary files, so, even if Ruby doesn't get a chance to delete it, the OS will as soon as the house-keeping chores run. – the Tin Man Jan 22 '14 at 17:01
  • @theTinMan Yeah, I understand that from the answer I accepted a while ago. – Peter Alfvin Jan 22 '14 at 17:10

2 Answers2

27

Not really. For the question itself, see this:

ObjectSpace.undefine_finalizer(tmpfile)

The Tempfile library uses Ruby ObjectSpace finalizers to automatically delete itself on garbage collection. By using the above line you can remove the Tempfile's ability to delete itself if you don't delete it. So, for example:

$ 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!
$ 

There's something else to be aware of though. Tempfile will use system temporary file directories like /tmp that the OS automatically cleans out every once in a while (for example on every boot). Because of this, even if you "persist" the file, you either need to be OK with it disappearing, or move it to a directory that doesn't get cleaned out by default, like /var/tmp (the Linux directory for persistant temporary files).


As for your second question, try this code from here:

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

It requires a require "tmpdir".

Community
  • 1
  • 1
Linuxios
  • 34,849
  • 13
  • 91
  • 116
  • @PeterAlfvin: Glad they helped. – Linuxios Jan 22 '14 at 15:23
  • The best solution is to NOT use TempFile for files that need to be permanent, and to create/delete them explicitly. TempFile is for files that are truly temporary and that can be easily recreated. – the Tin Man Jan 22 '14 at 17:03
  • @theTinMan Best for whom? See my comment on the question that begins "Like most of my SO questions ...". – Peter Alfvin Jan 22 '14 at 17:08
  • 1
    Best for those people who create a file using TempFile and then want to keep it after the fact. Instead, set a flag for debugging that uses the normal File class so you have persistence, then, when that flag isn't set use TempFile. Then you don't have to mess with the class itself. At a minimum you can use `$DEBUG` which is set when you pass `-d` to Ruby on the command line. – the Tin Man Jan 22 '14 at 17:20
  • Except that I don't know _which_ file among the hundreds or thousands of temporary files I'm creating I'm going to need to analyze later until some runtime condition occurs. In other words, I don't _know_ at creation if I'm going to need it afterwards. – Peter Alfvin Jan 22 '14 at 17:38
  • @Linuxios Assuming `Dir::Tmpname` creates filenames in the temporary directories as well, do you know of any way to create a unique filename in directory that the user specifies, using the same basename, suffix conventions that `Tempfile` uses? – Peter Alfvin Jan 22 '14 at 17:44
  • @peter: I have no idea. Sorry. Try a combo of time, PID, and a base name maybe? – Linuxios Jan 22 '14 at 19:38
12

I think the simplest solution may be to monkey patch the Tmpfile class to add a persist method. This method takes a filename where the temporary file will be moved to. Additionally, it removes the finalizer so that the temporary file will not be deleted at exit.

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

Running this program will create a persistent file ./hello.txt by moving the original temporary file instead of copying it.

Patrick Oscity
  • 53,604
  • 17
  • 144
  • 168