Ruby find out what mode file is opened in - ruby

I need to check what method a file object is opened in. E.g. is it r, r+, w, a etc.
thefile = File.open(filename, method)
It must be using the object thefile and not just the filename.

On POSIX platforms, you can call IO#fcntl with F_GETFL to get the file status flags:
require 'fcntl'
def filemode(io)
flags = io.fcntl(Fcntl::F_GETFL)
case flags & Fcntl::O_ACCMODE
when Fcntl::O_RDONLY
'r'
when Fcntl::O_WRONLY
(flags & Fcntl::O_APPEND).zero? ? 'w' : 'a'
when Fcntl::O_RDWR
(flags & Fcntl::O_APPEND).zero? ? 'r+ / w+' : 'a+'
end
end
File.open('test.txt', 'r') { |f| puts filemode(f) } #=> r
File.open('test.txt', 'w') { |f| puts filemode(f) } #=> w
File.open('test.txt', 'a+') { |f| puts filemode(f) } #=> a+
fcntl's return value is a bitwise OR of the individual O_* flags:
Fcntl::O_RDONLY # 0
Fcntl::O_WRONLY # 1
Fcntl::O_RDWR # 2
Fcntl::O_APPEND # 4
Fcntl::O_NONBLOCK # 8
Fcntl::O_ACCMODE can be used to mask the file access modes.
Further information:
http://man7.org/linux/man-pages/man2/fcntl.2.html
http://www.gnu.org/software/libc/manual/html_node/File-Status-Flags.html
http://ruby-doc.org/stdlib/libdoc/fcntl/rdoc/Fcntl.html

I am not going to write the full script for you, but will give you a hint.
Suppose you have an IO opened:
io = File.open("/tmp/foo", "r")
Assuming that io was created successfully, you can tell whether it is opened for writing by attempting write:
begin
io.write("")
rescue IOError => e
puts e.message
end
#=> not opened for writing
Make sure to copy the file before attempting this in order not to lose the file in case the mode was "w" or "w+".
Do along the same line for distinguishing other modes.

Related

ruby write all lines of puts in file?

I have my file
ppp.txt
mmm;2;nsfnjd;pet;
sadjjasjnsd;6;gdhjsd;pet;
gsduhdssdj;3;gsdhjhjsd;dog;
I need to write
nsfnjd
gsdhjhjsd
I use this code but only print the last line "gsdhjhjsd"
I dont know what is doing wrong
File.open("ppp.txt", "r") do |fi|
fi.readlines.each do |line|
parts = line.chomp.split(';')
if parts[1].to_i < 4
puts parts[2]
File.open("testxx.txt", "w+") do |f|
f. puts parts[2]
end
end
end
end
Please help me
Open the file using append mode, 'a+' instead of write mode 'w+', which overwrites the file, as the open command is called inside a loop.
Or open the write file prior to looping the lines of the read file.
open the file descriptor outside the loop
fo = File.open("testxx.txt","w+")
File.open("ppp.txt", "r") do |fi|
fi.readlines.each do |line|
parts = line.chomp.split(';')
fo.puts parts[2] if parts[1].to_i < 4
end
end
fo.close()
NOTE: Need to explicitly close fo, but file open with block; ruby close the file automatically (fi case).

Executing program from command line

I have done a program that sends requests to a url and saves them in a file. The program is this, and is working perfectly:
require 'open-uri'
n = gets.to_i
out = gets.chomp
output = File.open( out, "w" )
for i in 1..n
response = open('http://slowapi.com/delay/10').read
output << (response +"\n")
puts response
end
output.close
I want to modify it so that I can execute it from command line. I must run it like this:
fle --test abc -n 300 -f output
What must I do?
Something like this should do the trick:
#!/usr/bin/env ruby
require 'open-uri'
require 'optparse'
# Prepare the parser
options = {}
oparser = OptionParser.new do |opts|
opts.banner = "Usage: fle [options]"
opts.on('-t', '--test [STRING]', 'Test string') { |v| options[:test] = v }
opts.on('-n', '--count COUNT', 'Number of times to send request') { |v| options[:count] = v.to_i }
opts.on('-f', '--file FILE', 'Output file', :REQUIRED) { |v| options[:out_file] = v }
end
# Parse our options
oparser.parse! ARGV
# Check if required options have been filled, print help and exit otherwise.
if options[:count].nil? || options[:out_file].nil?
$stderr.puts oparser.help
exit 1
end
File::open(options[:out_file], 'w') do |output|
options[:count].times do
response = open('http://slowapi.com/delay/10').read
output.puts response # Puts the response into the file
puts response # Puts the response to $stdout
end
end
Here's a more idiomatic way of writing your code:
require 'open-uri'
n = gets.to_i
out = gets.chomp
File.open(out, 'w') do |fo|
n.times do
response = open('http://slowapi.com/delay/10').read
fo.puts response
puts response
end
end
This uses File.open with a block, which allows Ruby to close the file once the block exits. It's a much better practice than assigning the file handle to a variable and use that to close the file later.
How to handle passing in variables from the command-line as options is handled in the other answers.
The first step would be to save you program in a file, add #!/usr/bin/env ruby at the top and chmod +x yourfilename to be able to execute your file.
Now you are able to run your script from the command line.
Secondly, you need to modify your script a little bit to pick up command line arguments. In Ruby, the command line arguments are stored inside ARGV, so something like
ARGV.each do|a|
puts "Argument: #{a}"
end
allows you to retrieve command line arguments.

How to open and read files line-by-line from a directory?

I am trying to read file lines from a directory containing about 200 text files, however, I can't get Ruby to read them line-by-line. I did it before, using one text file, not reading them from a directory.
I can get the file names as strings, but I am struggling to open them and read each line.
Here are some of the methods I've tried.
Method 1:
def readdirectory
#filearray = []
Dir.foreach('mydirectory') do |i|
# puts i.class
#filearray.push(i)
#filearray.each do |s|
# #words =IO.readlines('s')
puts s
end#do
# puts #words
end#do
end#readdirectory
Method 2:
def tryread
Dir.foreach('mydir'){
|x| IO.readlines(x)
}
end#tryread
Method 3:
def tryread
Dir.foreach('mydir') do |s|
File.readlines(s).each do |line|
sentence =line.split
end#inner do
end #do
end#tryread
With every attempt to open the string passed by the loop function, I keep getting the error:
Permission denied - . (Errno::EACCES)
sudo ruby reader.rb or whatever your filename is.
Since permissions are process based you can not read files with elevated permissions if the process reading does not have them.
Only solutions are either to run the script with more permissions or call another process which is already running with higher permissions to read for you.
Thanks for all replies,I did a bit of trial and error and got it to work.This is the syntax I used
Dir.entries('lemmatised').each do |s|
if !File.directory?(s)
file = File.open("pathname/#{s}", 'r')
file.each_line do |line|
count+=1
#words<<line.split(/[^a-zA-Z]/)
end # inner do
puts #words
end #if
end #do
Try this one,
#it'll hold the lines
f = []
#here test directory contains all the files,
#write the path as per the your computer,
#mine's as you can see, below
#fetch filenames and keep in sorted order
a = Dir.entries("c:/Users/lordsangram/desktop/test")
#read the files, line by line
Dir.chdir("c:/Users/lordsangram/desktop/test")
#beginning for i = 1, to ignore first two elements of array a,
#which has no associated file names
2.upto(a.length-1) do |i|
File.readlines("#{a[i]}").each do |line|
f.push(line)
end
end
f.each do |l|
puts l
end
#the Tin Man -> you need to avoid processing "." and ".." which are listed in Dir.foreach and give the permission denied error. A simple if should fix all your apporoaches.
Dir.foreach(ARGV[0]) do |f|
if f != "." and f != ".."
# code to process file
# example
# File.open(ARGV[0] + "\\" + f) do |file|
# end
end
end

Ruby | binread : failed to allocate memory (NoMemoryError)

Running the following code
Dir.foreach(FileUtils.pwd()) do |f|
if f.end_with?('log')
File.open(f) do |file|
if File.size(f) > MAX_FILE_SIZE
puts f
puts file.ctime
puts file.mtime
# zipping the file
orig = f
Zlib::GzipWriter.open('arch_log.gz') do |gz|
gz.mtime = File.mtime(orig)
gz.orig_name = orig
gz.write IO.binread(orig)
puts "File has been archived"
end
#deleting the file
begin
File.delete(f)
puts "File has been deleted"
rescue Exception => e
puts "File #{f} can not be deleted"
puts " Error #{e.message}"
puts "======= Please remove file manually =========="
end
end
end
end
end
Also files are pretty heavy more than 1GB. Any help would be appreciated.
If the files you are reading are > 1GB, you have to have that much memory free at a minimum, because IO.binread is going to slurp that amount in.
You'd be better off to load a known amount and loop over the input until it's completely read, reading and writing in chunks.
From the docs:
IO.binread(name, [length [, offset]] ) -> string
------------------------------------------------------------------------------
Opens the file, optionally seeks to the given offset, then returns
length bytes (defaulting to the rest of the file). binread ensures
the file is closed before returning. The open mode would be "rb:ASCII-8BIT".
IO.binread("testfile") #=> "This is line one\nThis is line two\nThis is line three\nAnd so on...\n"
IO.binread("testfile", 20) #=> "This is line one\nThi"
IO.binread("testfile", 20, 10) #=> "ne one\nThis is line "

Ruby I/O - File Handle Left Open

Can anyone provide some clues as to why these two pieces of code are not equivalent? My only thought is that the .open in the latter code portion is not in a block and the file handle is left open.
File.open(file) do |io|
io.each_line do |line|
body_string << line
end
end
and this one
File.open(file).each_line {|line| body_string << line}
Thanks.
See IO class's API.
If File.open is given a block , it opens the file, executes the block, then closes the file.
If it is not given a block, it returns an object representing the file (just like File::new), so it might still be opened.
File test.rb:
def test1
body_string = []
[ File.open(ARGV[0]).each_line { |line| body_string << line }, body_string ]
end
def test2
body_string = []
[ File.open(ARGV[0]) do |io|
io.each_line { |line| body_string << line }
end, body_string ]
end
puts(test1.inspect)
puts(test2.inspect)
File f:
hello!
output of ruby test.rb f:
[#<File:f>, ["hello!\n"]]
[#<File:f (closed)>, ["hello!\n"]]
The only difference is that, when File.open is given a block, it automatically closes the file handle.
HTH

Resources