After reading a file ruby leaves it open/locked on Windows XP - ruby

I read a text file to get some info from it and later on I need to rename the directory that the file sits in. I am not able do to that because the file is locked. If I comment out the function that reads from the file or if I manually unlock the file (unlocker utility) everything goes fine.
I am on ruby 1.8.7 (2010-08-16 patchlevel 302) [i386-mingw32]
This line leaves the file open File.open(file).readlines.each{|line|
These two lines leave the file open
my_file=File.open(file,"r")
my_file.collect {|line|
unless I close the file at the end using my_file.close
The man for core 1.8.7 of IO.html#method-c-open states
If the optional code block is given, it will be passed io as an argument, and the IO object will automatically be closed when the block terminates.
So I don't understand why the file is still open.
What would be the one line code in 1.8.7 to read a text file and close it automatically?

The documentation is clear. However, you're passing the block to collect. And since you're not passing it to open, you are responsible for closing the file.
To have file auto-closed, try this:
File.open(file,"r") do |f|
f.collect # or whatever
end

Try passing the block directly to the "open" call:
File.open(file, 'r') do |f|
f.each_line do |line|
# Process each line
end
end
Or if you just want the file contents in a single shot then try this:
lines = File.read(file).split(/\r?\n/)

If you want the block to close the file automagically without passing the file handle to a block, you can use the IO#readlines method of your File object.
array_of_lines = File.readlines('/tmp/foo')
This will read the entire contents of the file, then close the file handle. It's a good option whenever a file is small enough to fit easily into memory.

Related

Failing to read a named pipe being written to

I have a stream of data that I’m writing to a named pipe:
named_pipe = '/tmp/pipe' # Location of named pipe
File.mkfifo(named_pipe) # Create named pipe
File.open(named_pipe, 'w+') # Necessary to not get a broken pipe when ⌃C from another process later on
system('youtube-dl', '--newline', 'https://www.youtube.com/watch?v=aqz-KE-bpKQ', out: named_pipe) # Output download progress, one line at a time
Trouble is, while I can cat /tmp/pipe and get the information, I’m unable to read the file from another Ruby process. I’ve tried File.readlines, File.read with seeking, File.open then reading, and other stuff I no longer remember. Some of those hang, others error out.
How can I get the same result as with cat, in pure Ruby?
Note I don’t have to use system to send to the pipe (Open3 would be acceptable), but any solution requiring external dependencies is a no-go.
it looks like File.readlines/IO.readlines, File.read/IO.read need to load the whole temp file first so you don't see any be printed out.
try File#each/IO.foreach which process a file line by line and it does not require the whole file be loaded into memory
File.foreach("/tmp/pipe") { |line| p line }
# or
File.open('/tmp/pipe','r').each { |line| p line }

Ruby: Reading from a file written to by the system process

I'm trying to open a tmpfile in the system $EDITOR, write to it, and then read in the output. I can get it to work, but I am wondering why calling file.read returns an empty string (when the file does have content)
Basically I'd like to know the correct way of reading the file once it has been written to.
require 'tempfile'
file = Tempfile.new("note")
system("$EDITOR #{file.path}")
file.rewind
puts file.read # this puts out an empty string "" .. why?
puts IO.read(file.path) # this puts out the contents of the file
Yes, I will be running this in an ensure block to nuke the file once used ;)
I was running this on ruby 2.2.2 and using vim.
Make sure you are calling open on the file object before attempting to read it in:
require 'tempfile'
file = Tempfile.new("note")
system("$EDITOR #{file.path}")
file.open
puts file.read
file.close
file.unlink
This will also let you avoid calling rewind on the file, since your process hasn't written any bytes to it at the time you open it.
I believe IO.read will always open the file for you, which is why it worked in that case. Whereas calling .read on an IO-like object does not always open the file for you.

File not being created in Ruby script

I am trying to open a non existent file and write to it, however when I run the script, no file is being created.
Here is the line of code
File.open("valid_policies.txt", 'a+').write(policy_number.to_s + "\n")
Instead of using .write try this instead:
File.open("valid_policies.txt", 'a+') {|f| f.write(policy_number.to_s + "\n") }
You're using:
File.open("valid_policies.txt", 'a+').write(policy_number.to_s + "\n")
That's a non-block form of open which doesn't automatically close the file. That means the data is most likely not being written to the file but is sitting in the IO buffer waiting to be flushed/synced. You could add a close but that only propagates non-idiomatic code.
Instead you can use:
File.write("valid_policies.txt", policy_number.to_s + "\n")
File.write automatically creates then writes to the file then closes it. It will overwrite existing files though.
If you aren't sure whether the file exists and want to create it if it doesn't, or append to it, then you use File.open with the a mode instead of a+. From the mode documentation:
"a" Write-only, each write call appends data at end of file.
Creates a new file for writing if file does not exist.
Using a+ will work but it unnecessarily opens the file for reading also. Don't do that unless you're sure that's what you have to do.
If I needed to append I'd use:
File.open('valid_policies.txt', 'a') do |fa|
fa.puts policy_number
end
That's idiomatic Ruby. puts will automatically "stringify" policy_number if it has a to_s method, which it should have since you're already calling it, and it'll also automatically add the trailing "\n" if it doesn't exist at the end of the string. Also, using the block form of open will automatically close the file when the block exists, which is smart house-keeping.

Only "puts" one line to a text document

The code I'm working with at the moment is supposed to spit back every line of information in one text document that contains the word "DEBUG" and then paste it in a new text document titled "debug.txt".
For whatever reason it is only printing the final line into the new text document and I have no clue why. However, another function is to spit back every line to the command terminal, and it does that successfully, it just won't write them all to the file.
log_file = File.open("main_file.rb")
File.readlines(log_file).each do |line|
if line.include? "DEBUG"
puts line
File.open("debug.txt", "w") do |out|
out.puts line
end
end
end
You're overwriting the file every time you find a DEBUG line in main_file. You have your blocks backwards. The File.open('debug.txt') should be outside of the File.readlines.
Like this:
log_file = File.open("main_file.rb")
File.open("debug.txt", "w") do |out|
File.readlines(log_file).each do |line|
if line.include? "DEBUG"
puts line
out.puts line
end
end
end
You could also open the file in append mode by passing 'a' instead of 'w' in your File.open('debug.txt') call but this would be needlessly reopening the file every time you find a line that contains DEBUG in it. It would be better to open the debug file once for writing and using the file handle from there on as I show above.
Write it like this:
File.open("debug.txt", "w") do |out|
File.foreach("main_file.rb") do |line|
if line['DEBUG']
puts line
out.puts line
end
end
end
You need to:
Open the output file.
Iterate over the lines in the input file.
For each line, check to see if it contains the string you want.
If so, write it.
Loop until the input file is completely read.
Close the output file.
Notice I don't open the file for output as a single step. Ruby's use of blocks are really handy: By passing a block to open, Ruby will close the file when the block exits, avoiding the problem of open files hanging around to clutter memory or consume available file handles.
Use foreach to read the file. It reads a single line at a time and is extremely fast. It's also scalable, which means it'll work for a one-line file or a 10-million line file equally well. Using readlines, as in your code, results in Ruby loading the entire file into memory, splitting it into separate lines, then iterating over them. That can cause real problems if your input file exceeds available RAM.
line['DEBUG'] is shorthand for "do a substring match for this text". See String#[] for more information.

Open a file, read the lines, find a certain line and append a string to the end of it in ruby

So I want to read in my .bash_profile and append a string to the PATH.
Should I be opening the file and reading per line until I find what I want then replace? Or read in everything first?
File.open("/root/.bash_profile", "w+") do |file|
while line = line.gets
if line =~ /^PATH/
Not sure how to append
end
end
The w+ mode for files erases all content (I found this in a script that tried to modify its source). If you want to be able to write but keep content, use the r+ mode instead.
NOTE: After seing your problem, why can you not append a line to this effect to the end of the bash profile?:
PATH=/some/path:$PATH
Or will this not work? Because the code for that is simple:
f=File.new("~/.bash_profile", "a+")
f.puts "PATH=/some/path:$PATH"
This may work just as well.

Resources