Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read an open file in Ruby

Tags:

I want to be able to read a currently open file. The test.rb is sending its output to test.log which I want to be able to read and ultimately send via email.

I am running this using cron:

*/5 * * * /tmp/test.rb > /tmp/log/test.log 2>&1 

I have something like this in test.rb:

#!/usr/bin/ruby  def read_file(file_name)   file = File.open(file_name, "r")   data = file.read   file.close   return data end  puts "Start" puts read_file("/tmp/log/test.log") puts "End" 

When I run this code, it only gives me this output:

Start  End 

I would expect the output to be something like this:

Start Start (from the reading of the test.log since it should have the word start already) End 
like image 243
Carmen Avatar asked Dec 18 '10 00:12

Carmen


People also ask

What does __ file __ mean in Ruby?

The value of __FILE__ is a relative path that is created and stored (but never updated) when your file is loaded. This means that if you have any calls to Dir.

How do you read a line by line in Ruby?

Overview. The IO instance is the basis for all input and output operations in Ruby. Using this instance, we can read a file and get its contents line by line using the foreach() method. This method takes the name of the file, and then a block which gives us access to each line of the contents of the file.


1 Answers

Ok, you're trying to do several things at once, and I suspect you didn't systematically test before moving from one step to the next.

First we're going to clean up your code:

def read_file(file_name)   file = File.open(file_name, "r")   data = file.read   file.close   return data end  puts "Start" puts read_file("/tmp/log/test.log") puts "End" 

can be replaced with:

puts "Start" puts File.read("./test.log") puts "End" 

It's plain and simple; There's no need for a method or anything complicated... yet.

Note that for ease of testing I'm working with a file in the current directory. To put some content in it I'll simply do:

echo "foo" > ./test.log 

Running the test code gives me...

Greg:Desktop greg$ ruby test.rb  Start foo End 

so I know the code is reading and printing correctly.

Now we can test what would go into the crontab, before we deal with its madness:

Greg:Desktop greg$ ruby test.rb > ./test.log  Greg:Desktop greg$  

Hmm. No output. Something is broken with that. We knew there was content in the file previously, so what happened?

Greg:Desktop greg$ cat ./test.log  Start  End 

Cat'ing the file shows it has the "Start" and "End" output of the code, but the part that should have been read and output is now missing.

What happening is that the shell truncated "test.log" just before it passed control to Ruby, which then opened and executed the code, which opened the now empty file to print it. In other words, you're asking the shell to truncate (empty) it just before you read it.

The fix is to read from a different file than you're going to write to, if you're trying to do something with the contents of it. If you're not trying to do something with its contents then there's no point in reading it with Ruby just to write it to a different file: We have cp and/or mv to do those things for us witout Ruby being involved. So, this makes more sense if we're going to do something with the contents:

ruby test.rb > ./test.log.out 

I'll reset the file contents using echo "foo" > ./test.log, and cat'ing it showed 'foo', so I'm ready to try the redirection test again:

Greg:Desktop greg$ ruby test.rb > ./test.log.out Greg:Desktop greg$ cat test.log.out  Start foo End 

That time it worked. Trying it again has the same result, so I won't show the results here.

If you're going to email the file you could add that code at this point. Replacing the puts in the puts File.read('./test.log') line with an assignment to a variable will store the file's content:

contents = File.read('./test.log') 

Then you can use contents as the body of a email. (And, rather than use Ruby for all of this I'd probably do it using mail or mailx or pipe it directly to sendmail, using the command-line and shell, but that's your call.)

At this point things are in a good position to add the command to crontab, using the same command as used on the command-line. Because it's running in cron, and errors can happen that we'd want to know about, we'd add the 2>&1 redirect to capture STDERR also, just as you did before. Just remember that you can NOT write to the same file you're going to read from or you'll have an empty file to read.

That's enough to get your app working.

like image 197
the Tin Man Avatar answered Oct 19 '22 23:10

the Tin Man