Why do i get this error 'ruby: No such file or directory -- Readingfile.rb (LoadError)' when i run my ruby program to read files?
My code:
filename = ARGV.first
txt = open(filename)
puts "Heres your file#{filename}:"
print txt.read
print "TYpe the filename again"
file_again = $stdin.gets.chomp
txt_again = open(file_again)
print txt_again.read
'ruby: No such file or directory -- Readingfile.rb (LoadError)'
Without a stack trace, I can only infer that this error originates from the line txt = open(filename). What's most likely is that the filename (first arg passed into $ ruby file.rb) either does not exist or was unspecified (and therefore nil).
In order to ensure that your program is resilient to different kinds of input, you should check and handle cases where no valid filename is passed. This can be done with File#exist?:
puts "Missing filename" and exit! unless filename = ARGV.first
Related
I'm trying this script to rename a series of files with unwanted characters:
$stdout.sync
print "Enter the file search query: "; search = gets.chomp
print "Enter the target to replace: "; target = gets.chomp
print " Enter the new target name: "; replace = gets.chomp
Dir['*'].each do |file|
# Skip directories
next unless File.file?(file)
old_name = File.basename(file,'.*')
if old_name.include?(search)
# Are you sure you want gsub here, and not sub?
# Don't use `old_name` here, it doesn't have the extension
new_name = File.basename(file).gsub(target,replace)
File.rename( file, new_path )
puts "Renamed #{file} to #{new_name}" if $DEBUG
end
end
I would like to be able to pass as a prompt argument the path of the directory that contains the files to be renamed, and then I modified the script as follows:
$stdout.sync
path = ARGV[0]
print "Enter the file search query: "; search = gets.chomp
print "Enter the target to replace: "; target = gets.chomp
print " Enter the new target name: "; replace = gets.chomp
Dir[path].each do |file|
# Skip directories
next unless File.file?(file)
old_name = File.basename(file,'.*')
if old_name.include?(search)
# Are you sure you want gsub here, and not sub?
# Don't use `old_name` here, it doesn't have the extension
new_name = File.basename(file).gsub(target,replace)
File.rename( file, new_path )
puts "Renamed #{file} to #{new_name}" if $DEBUG
end
end
get this error message:
renamefiles.rb:3:in `gets': Is a directory # io_fillbuf - fd:7
why?
When you pass an argument such that ARGV is populated the ruby interpreter will assume you mean Kernel#gets which expects a filename.
You should be able to fix this by using STDIN.gets so you would have
print "Enter the file search query: "; search = STDIN.gets.chomp
print "Enter the target to replace: "; target = STDIN.gets.chomp
print " Enter the new target name: "; replace = STDIN.gets.chomp
I have refined the code so that the file extension is not changed, and the directories are also renamed.
I have two problems left to solve:
-the passage of the path from argv (the path is not correctly recognized)
-I would like to recursively rename, even files in directories
path = ARGV[0]
print "Enter the file search query: "; search = gets.chomp
print "Enter the target to replace: "; target = gets.chomp
print " Enter the new target name: "; replace = gets.chomp
Dir::chdir('/Users/dennis/Documents/test/daRinominare')
Dir['*'].each do |file|
#puts file
if Dir.exist?(file)
directoryList = file
old_name = File.basename(file)
new_name = old_name.gsub(target,replace)
File.rename( file, new_name)
end
next unless File.file?(file)
old_name = File.basename(file,'.*')
extension = File.extname(file)
if old_name.include?(search)
new_name = old_name.gsub(target,replace) + extension
File.rename( file, new_name)
puts "Renamed #{file} to #{new_name}" if $DEBUG
end
end
Kernel.gets reads from ARGF, which acts as an aggregate IO to read from the files named in ARGV, unless ARGV is empty in which case ARGF reads from $stdin. ARGF.gets will generate errors like EISDIR and ENOENT if ARGV has entries which are paths to directories or paths that don't exist.
If you want to read user input, use $stdin.gets
(The difference between $stdin and STDIN: The constant STDIN is the process standard input stream, and is the initial value of the variable $stdin which can be reassigned to change the source used by library methods; see globals. I use $stdin unless I need to change $stdin and also use STDIN for another purpose.)
I have a program that tries to open a file:
Dir.chdir(File.dirname(__FILE__))
puts "Enter file name: ";
relPath = gets;
absPath = Dir.pwd << "/" << relPath;
if File.exist?(absPath) then
puts "File exists";
file = File.open(absPath, "r");
other code...
else
puts "File does not exist";
end
It always prints "File does not exist" even when the current directory exists and the file also exists. The file and script are in the same directory.
I am running it on Mac OS X Yosemite (10.10.3) and Ruby 2.2.0p0.
I can't explain why (albeit I have strong belief that it's for some whitespace characters) but with this little contribution it works ok.
Dir.chdir(File.dirname(__FILE__))
print "Enter file name:";
relPath = gets.chomp; #intuitively used this, and it wroked fine
absPath = File.expand_path(relPath) #used builtin function expand_path instead of string concatenation
puts absPath
puts File.file?(absPath)
if File.exist?(absPath) then
puts "File exists";
puts File.ctime(absPath) #attempting a dummy operation :)
else
puts "File does not exist";
end
runnning code
$ ls -a anal*
analyzer.rb
$ ruby -v
ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-linux]
ziya#ziya:~/Desktop/code/ruby$ ruby fileexists.rb
Enter file name:analyzer.rb
/home/ziya/Desktop/code/ruby/analyzer.rb #as a result of puts absPath
true #File.file?(absPath) => true
File exists
2015-06-11 12:48:31 +0500
That code has syntax error ("if" doesnt need "then"), and you dont have to put ";" after each line.
try
Dir.chdir(File.dirname(__FILE__))
puts "Enter file name: "
relPath = gets
absPath = "#{Dir.pwd}/#{relPath.chop}"
if File.exist?(absPath)
puts "File exists"
file = File.open(absPath, "r")
else
puts "File does not exist"
end
remember that gets will add a new line character so you will need to do a chomp, and that way to concatenate string won't work on ruby.
Your code is not idiomatic Ruby. I'd write it something like this untested code:
Dir.chdir(File.dirname(__FILE__))
puts 'Enter file name: '
rel_path = gets.chomp
abs_path = File.absolute_path(rel_path)
if File.exist?(abs_path)
puts 'File exists'
File.foreach(abs_path) do |line|
# process the line
end
else
puts 'File does not exist'
end
While Ruby supports the use of ;, they're for use when we absolutely must provide multiple commands on one line. The ONLY time I can think of needing that is when using Ruby to execute single-line commands at the command-line. In normal scripts I've never needed ; between statements.
then is used with if when we're using a single line if expression, however, we have trailing if which removes the need for then. For instance, these accomplish the same thing but the second is idiomatic, shorter, less verbose and easier to read:
if true then a = 1 end
a = 1 if true
See "What is the difference between "if" statements with "then" at the end?" for more information.
Instead of relPath and absPath we use snake_case for variables, so use rel_path and abs_path. It_is_a_readability AndMaintenanceThing.
File.absolute_path(rel_path) is a good way to take the starting directory and return the absolute path given a relative directory.
File.foreach is a very fast way to read a file, faster than slurping it using something like File.read. It is also scalable whereas File.read is not.
I wrote a script to read IP addresses from a file and print the amount in the file. I wasn't fully satisfied so I attempted to modify it to allow reading multiple files, and I would specify the files via cmd arguments. The problem that I'm having is that it seems to read multiple files as one argument.
def host_count(*files)
begin
files.each do
files = files.join(' ')
read = IO.read(files)
reg = read.scan(/(?:\d{1,3}\.){3}\d{1,3}/).size
puts "There are " << reg.to_s << " IP addresses in #{files}."
end
rescue Errno::ENOENT
puts "File #{files} does not exist!"
rescue TypeError
puts "Usage: #{$0} [file]"
puts "Example: #{$0} /home/user/ipfile.txt"
end
end
host_count(ARGV)
Running this script with multiple files gives me this error:
File file1 file2 does not exist!
They aren't separated by commas or anything, so it's not reading my arguments as: ["file1","file2"], which was my original problem. What am I not understanding?
You wrote
files.each do
files = files.join(' ')
Why would you do that?
you're changing the array..
the "files" array is already an array, you don't have to join it with strings.
edit1:
to get the specific file for each run, you should write:
files.each do |file|
puts file # will print "file 1", and in the next iteration will print "file 2".
end
I have created one ruby script that I want to run with some flags on console say -v flag prints output on console and -o stores output in new file with file name I am taking from console using gets()
My code has following structure:
puts "Enter filename to analyze:\n\n"
filename = gets().chomp
puts "Provide filename to store result in new text file:\n\n"
output = gets().chomp
filesize = File.size(filename)
puts "File size in Bytes:\n#{filesize.to_i}\n"
pagecontent = filesize - 20
puts "\n\nData:\n#{pagecontent}\n\n"
File.open(filename,'r') do |file|
#whole process with few do..end in between that I want to do in 2 different #ways.
#If I provide -v flag on console result of this code should be displayed on console
#and with -o flag it should be stored in file with filename provided on console #stored in output variable declared above
end
end
Use stdlib OptionParser
In OptionParser I can make an option mandatory, but if I leave out that value it will take the name of any following option as the value, screwing up the rest of the command line parsing.
Here is a test case that echoes the values of the options:
$ ./test_case.rb --input foo --output bar
output bar
input foo
Now leave out the value for the first option:
$ ./test_case.rb --input --output bar
input --output
Is there some way to prevent it taking another option name as a value?
Thanks!
Here is the test case code:
#!/usr/bin/env ruby
require 'optparse'
files = Hash.new
option_parser = OptionParser.new do |opts|
opts.on('-i', '--input FILENAME', 'Input filename - required') do |filename|
files[:input] = filename
end
opts.on('-o', '--output FILENAME', 'Output filename - required') do |filename|
files[:output] = filename
end
end
begin
option_parser.parse!(ARGV)
rescue OptionParser::ParseError
$stderr.print "Error: " + $! + "\n"
exit
end
files.keys.each do |key|
print "#{key} #{files[key]}\n"
end
What you want to do is not a good idea. What if you really have a file named "--output"? This is a perfectly valid filename on Unix. Every Unix program's option parsing works the way the ruby one is doing, so you shouldn't change it, because then your program will be arbitrarily different from everything else, which is confusing and violates the "principle of least surprise."
The real question is: why are you having this problem in the first place? Perhaps you're running your program from another program, and the parent program is providing a blank filename as the parameter to --input, which makes it see --output as the parameter to --input. You can work around this by always quoting the filenames you pass on the command line:
./test_case.rb --input "" --output "bar"
Then --input will be blank, and that's easy to detect.
Also note that if --input is set to --output (and --output is not a real file) you can just try to open the --input file. If it fails, print a message like:
can't open input file: --output: file not found
And that should make it clear to the user what they did wrong.
try this:
opts.on('-i', '--input FILENAME', 'Input filename - required') do |filename|
files[:input] = filename
end
opts.on('-o', '--output FILENAME', 'Output filename - required') do |filename|
files[:output] = filename
end
opts.on("-h", "--help", "Show this message") do
puts opts
exit
end
begin
ARGV << "-h" if ARGV.size != 2
option_parser.parse!(ARGV)
rescue OptionParser::ParseError
$stderr.print "Error: " + $! + "\n"
exit
end
In this case, the mandatory --output option is missing, so do this after calling parse!:
unless files[:input] && files[:output]
$stderr.puts "Error: you must specify both --input and --output options."
exit 1
end
OK - this works - the regular expression in the on() call allows any string as long as it doesn't start with a '-'
If I don't pass an argument to --input and there is another option downstream then it will take that option key as the argument to --input. (e.g. --input --output). The regexp catches that and then I check the error message. If the argument it reports starts with '-' I output the correct error message, namely that there is a missing argument. Not pretty but it seems to work.
Here is my working test case:
#!/usr/bin/env ruby
require 'optparse'
files = Hash.new
option_parser = OptionParser.new do |opts|
opts.on('-i FILENAME', '--input FILENAME', /\A[^\-]+/, 'Input filename - required') do |filename|
files[:input] = filename
end
opts.on('-o FILENAME', '--output FILENAME', /\A[^\-]+/, 'Output filename - required') do |filename|
files[:output] = filename
end
end
begin
option_parser.parse!(ARGV)
rescue OptionParser::ParseError
if $!.to_s =~ /invalid\s+argument\:\s+(\-\-\S+)\s+\-/
$stderr.print "Error: missing argument: #{$1}\n"
else
$stderr.print "Error: " + $! + "\n"
end
exit
end
files.keys.each do |key|
print "#{key} #{files[key]}\n"
end