3

So what I am trying to do is pass a file name into a method and and check if the file is closed. What I am struggling to do is getting a file object from the file name without actually opening the file.

def file_is_closed(file_name) file = # The method I am looking for file.closed? end 

I have to fill in the commented part. I tried using the load_file method from the YAML module but I think that gives the content of the file instead of the actual file.

I couldn't find a method in the File module to call. Is there a method maybe that I don't know?

4
  • 2
    You can't get a file handle to a file without opening it. But you also don't need to have a handle to see if a file is open if you have the lsof command available in your OS. My question is, why don't you know if the file is open or closed and/or, why do you need to know? What are you trying to accomplish? Perhaps if we knew that we could give a better way of doing what you want to do. In general it's bad practice to have files open longer than they need to be and you should use the block form when opening file to automatically close them. Commented Jun 7, 2013 at 4:17
  • You can't even necessarily use lsof, I don't think. Files can be renamed and such after they are opened. Pretty sure a filename doesn't even have to refer to a distinct and unique file. Basically as asked, you can't do what you're asking. Commented Jun 7, 2013 at 4:37
  • I want to have this method in my test file to call after every test to make sure that the file is closed. I am actually calling file.close after every method that uses the file but I want to make sure that's the case in the test as well. So what I am doing is call a module function in the test so I don't have access to the actual file either. I mean I could return it from the method but I am not going to hack a piece of code to help testing Commented Jun 7, 2013 at 5:54
  • Use File.open with a block and the file is closed automatically. Commented Jun 7, 2013 at 7:54

2 Answers 2

1

File#closed? returns whether that particular File object is closed, so there is no method that is going to make your current attempted solution work:

f1 = File.new("test.file") f2 = File.new("test.file") f1.close f1.closed? # => true # Even though f2 still has the same file open 

It would be best to retain the File object that you're using in order to ask it if it is closed, if possible.

If you really want to know if your current Ruby process has any File objects open for a particular path, something like this feels hack-ish but should mostly work:

def file_is_closed?(file_name) ObjectSpace.each_object(File) do |f| if File.absolute_path(f) == File.absolute_path(file_name) && !f.closed? return false end end true end 

I don't stand by that handling corner cases well, but it seems to work for me in general:

f1 = File.new("test.file") f2 = File.new("test.file") file_is_closed?("test.file") # => false f1.close file_is_closed?("test.file") # => false f2.close file_is_closed?("test.file") # => true 

If you want to know if any process has the file open, I think you'll need to resort to something external like lsof.

Sign up to request clarification or add additional context in comments.

5 Comments

"without opening the file".
@Phrogz Yep, I got that. My solution does not open the file. My example at the top obviously opens files in order to demonstrate what File#closed? does. My demonstration at the bottom opens and closes the file, again, to demonstrate that it works. The solution, file_is_closed?, dose not open the file.
You are assuming the File handlers are in the current Ruby process. If my understanding is correct, the OP wants to inspect if other processes have a handle open to the file, not Ruby.
@Phrogz I thought it was obvious that this would only work as far as the current process, but I've now made that explicit. It's not clear to me what the OP wants, but I was guessing it was only about the current process. I've added back my original line about lsof in case they care about other processes.
this is actually good enough. i am not working on something enterprise strength. its just a small thing i am doing on the side so thank you Darshan Computing for the answer
0

For those cases where you no longer have access to the original file objects in Ruby (after fork + exec, for instance), a list of open file descriptors is available in /proc/pid/fd. Each file there is named for the file descriptor number, and is a symlink to the opened file, pipe, or socket:

# Returns hash in form fd => filename def open_file_descriptors Hash[ Dir.glob( File.join( '/proc', Process.pid.to_s, 'fd', '*' ) ). map { |fn| [File.basename(fn).to_i, File.readlink(fn)] rescue [nil, nil] }. delete_if { |fd, fn| fd.nil? or fd < 3 } ] end # Return IO object for the named file, or nil if it's not open def io_for_path(path) fd, fn = open_file_descriptors.find {|k,v| path === v} fd.nil? ? nil : IO.for_fd(fd) end # close an open file file = io_for_path('/my/open/file') file.close unless file.nil? 

The open_file_descriptors method parses the fd directory and returns a hash like {3 => '/my/open/file'}. It is then a simple matter to get the file descriptor number for the desired file, and have Ruby produce an IO object for it with for_fd.

This assumes you are on Linux, of course.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.