Ruby: File not found exception, even though it exists, and is passed down by loop - ruby

I'm practicing Ruby after growing to resent Java with a simple little program that determines how similar a few thousand documents are. I did the same thing in Java junior year of high school for a data structures project, and actually wrote the fastest program the teacher had ever seen (apparently), but somehow I can't transfer that over to Ruby.
I'm trying to create an array of files:
array = Dir.foreach("/home/rj/Documents/Ruby Code/Catching Plaigarists/Large number of documents") do |filename|
File.new(filename, "r") unless filename.start_with?(".") #Prevents it from returning current and parent directories
end
When I run it in the terminal, I get this rather irritating exception:
/home/rj/Documents/Ruby Code/Catching Plaigarists/y_don_it_work.rb:2:in `initialize': No such file or directory # rb_sysopen - bmu390.shtml.txt (Errno::ENOENT)
from /home/rj/Documents/Ruby Code/Catching Plaigarists/y_don_it_work.rb:2:in `new'
from /home/rj/Documents/Ruby Code/Catching Plaigarists/y_don_it_work.rb:2:in `block in <main>'
from /home/rj/Documents/Ruby Code/Catching Plaigarists/y_don_it_work.rb:1:in `foreach'
from /home/rj/Documents/Ruby Code/Catching Plaigarists/y_don_it_work.rb:1:in `<main>'
I checked to make sure the document existed. It did. Ran it again a few more times. Same thing with the same document. Naturally, I tried deleting the document. But then it did the same thing for some other document. And when I deleted that document, it managed to find some problem with some other document with a higher alphabetical precedence that it seemed to do just fine with the first two times around.
Why can't it find these documents, and if they don't exist, why does the loop pass the filenames down in the first place? What am I doing wrong? Why me?
(Also I'm running Fedora with Cinnamon, if that would have any impact.)

Your working directory is different than the directory your looping through. You could store the path your working with as a string and then concatenate it to the filename you get back from your loop.
path = "/home/rj/Documents/Ruby Code/Catching Plaigarists/Large number of documents/"
array = Dir.foreach(path) do |filename|
File.new((path + filename), "r") unless filename.start_with?(".")
end
You'll need the slash at the end of the path variable to build the correct path.

Related

Dir.chdir(File.dirname(__FILE__)) throws Errno::ENOENT

I got a method where I use Dir.chdir(File.dirname(__FILE__)) inside. I am using it so that I can run the ruby file from anywhere and not get this error: No such file or directory # rb_sysopen - 'filename' (Errno::ENOENT).
Using the method for the first time works fine but using it for the second time throws an error. See method and exact error below.
def meth(string)
Dir.chdir(File.dirname(__FILE__))
hash = JSON.parse(File.read("file.json"))
# do something with hash and string
# return some value
end
meth("first string") # this returns what is expected
meth("second string") # this second usage of the method throws the error
Error sample pinpointing the line where I used Dir.chdir(File.dirname(__FILE__)):
dir/decoder.rb:44:in `chdir': No such file or directory # dir_s_chdir - lib (Errno::ENOENT)
Not sure if OS plays a role here, I am using an m1 BigSur on version 11.2.3.
Why is this happening?
What needs to be done so that the method` can be used as much as needed without running into the error?
Your problem here seems to be that __FILE__ is a relative path like dir/decoder.rb and that path becomes invalid after the first time Dir.chdir is used, because that command changes the working directory of your entire Ruby process. I think the solution would be to do something like this in your decoder.rb file:
DecoderDir = File.realpath(File.dirname(__FILE__))
def meth
Dir.chdir(DecoderDir)
# ...
end
I'm guessing that the first time the Ruby interpreter processes the file, that is early enough that the relative path in __FILE__ still refers to the right place. So, at that time, we generate an absolute path for future use.
By the way, a well-behaved library should not run Dir.chdir because it will affect all use of relative paths throughout your entire Ruby process. I pretty much only run Dir.chdir once and I run it near the beginning of my top-level script. If you're making a reusable library, you might want to do something like this to calculate the absolute path of the file you want to open:
DecoderJson = File.join(DecoderDir, 'file.json')

Errors with getting file permissions with File.Stat (Errno::ENOENT)

I'm new to programming and I am trying to figure out how to grab all the recursive directories of my array, and get the file permissions of all of them.
Googling was pointing me to file.stat, however the code is stating there is no such file or directory called 'stat'. For context, after getting the file permissions, my next objective is to be able to compare the permissions of the file owner to the group to determine any differences. I think part of my problem displayed with 'File::Stat:0x7f2407a9e908' is that it's not converting to an integer.
Any suggestions on reading material or revision would be greatly appreciated.
Here is my code so far:
%w(/etc /bin /usr/bin /usr/lbin /usr/usb /sbin /usr/sbin).each do |dir|
Dir.glob("#{dir}/**").each do |c|
s = File.stat("#{c}")
puts s
end
end
And here is my return:
#<File::Stat:0x7f2407a9e908>
test.rb:3:in `stat': No such file or directory - /usr/bin/chef-zero (Errno::ENOENT)
from test.rb:3
from test.rb:2:in `each'
from test.rb:2
from test.rb:1:in `each'
from test.rb:1

How can I copy files and rename them according to their origin directories using Ruby?

I have many directories with generically named txt files inside. I want to make copies of the txt files, rename them according to the containing directory of each, then move them to the parent directory (that being the directory that holds the directories that hold the original txt files, designated "txts" in the script below). I want to retain the original txt files with their original names in their original directories as well so that nothing within the original directories changes.
I have an old script that I think achieved (some of) my goals once, perhaps moving instead of copying the original txt files, but I'm unable to run it successfully now:
require 'find'
require 'fileutils'
Find.find("txts") do |path|
if FileTest.directory?(path)
next
end
ret = path.scan(/.*txts\/([^\/]+)\/.*/)
name = ret[0].to_s + ".txt"
FileUtils.mv(path, name)
end
Years ago a friend wrote this and ran it from within a unix environment with success. When I run it now, an enormous number of errors are returned. I'm using Ruby 2.2.2 and it's entirely possible there's a placeholder somewhere that I'm too newbish to recognize, or perhaps something changed from the older version of FileUtils... I truly have no idea and am afraid I've been unable to turn up any answers with my neophyte skills.
And so I appeal to you...
Edit: Here's the error message:
C:/Ruby22/lib/ruby/2.2.0/fileutils.rb:1328:in `stat': Invalid argument # rb_file
_s_stat - ["may2013"].txt (Errno::EINVAL)
from C:/Ruby22/lib/ruby/2.2.0/fileutils.rb:1328:in `lstat'
from C:/Ruby22/lib/ruby/2.2.0/fileutils.rb:1247:in `exist?'
from C:/Ruby22/lib/ruby/2.2.0/fileutils.rb:519:in `block in mv'
from C:/Ruby22/lib/ruby/2.2.0/fileutils.rb:1570:in `block in fu_each_src
_dest'
from C:/Ruby22/lib/ruby/2.2.0/fileutils.rb:1586:in `fu_each_src_dest0'
from C:/Ruby22/lib/ruby/2.2.0/fileutils.rb:1568:in `fu_each_src_dest'
from C:/Ruby22/lib/ruby/2.2.0/fileutils.rb:516:in `mv'
from extracttxt.rb:12:in `block in <main>'
from C:/Ruby22/lib/ruby/2.2.0/find.rb:48:in `block (2 levels) in find'
from C:/Ruby22/lib/ruby/2.2.0/find.rb:47:in `catch'
from C:/Ruby22/lib/ruby/2.2.0/find.rb:47:in `block in find'
from C:/Ruby22/lib/ruby/2.2.0/find.rb:42:in `each'
from C:/Ruby22/lib/ruby/2.2.0/find.rb:42:in `find'
from extracttxt.rb:6:in `<main>'
The error message shows that ret[0] is the array [ "may13" ], so ret[0].to_s + ".txt" evaluates to the string ["may13"].txt. I'm not sure, but it's possible the behavior of String#scan changed in Ruby 1.9 or 2.0, so it returns an array of arrays when captures are present, whereas before it returned an array of strings.
Something like this ought to solve the problem:
require 'find'
require 'fileutils'
Find.find("txts") do |path|
if FileTest.directory?(path)
next
end
if path =~ %r{txts/([^/]+)/}
FileUtils.cp(path, "#{$1}.txt")
end
end
If you want to match by file extension you could either add it to the Regexp above (e.g. %r{txts/([^/]+)/.+\.txt$}) or you could use Dir[] (a.k.a. Dir.glob) e.g.:
require 'dir'
require 'fileutils'
Dir['txts/**/*.txt'].each do |path|
next if FileTest.directory?(path) ||
next unless path =~ %r{txts/([^/]+)/}
FileUtils.cp(path, "#{$1}.txt")
end
I don't know if there will be any performance difference, but it might be worth trying.

Search and Replace within one file

i'm new to Ruby and i'm trying to use RegEx to do multiple search and replaces in an input text file, however my code isn't working, i think i understand why it doesn't work but i don't know the syntax i need to make it work.
Heres my code:
# encoding: utf-8
#!/usr/bin/ruby
# open file to read and write
file = File.open("input.txt", "r+")
# get the contents of the file
contents = file.read
file.close
reassign = contents.gsub(/\w+/, '£££££')
# save it out as a new file
new_file = File.new("output.txt")
new_file.write(reassign)
new_file.close
this is the error messages:
C:/Users/parsonsr/RubymineProjects/Test 3/test3.rb:14:in `write': not opened for writing (IOError)
from C:/Users/parsonsr/RubymineProjects/Test 3/test3.rb:14:in `<top (required)>'
from -e:1:in `load'
from -e:1:in `<main>'
i tried using an array to pass each line through and change whats relevant but then it only saves whats in the array to the output not the rest of the file.
I either need it to change the text thats already there within one file or change the text then save the file with the new changes made into an output file, whichever is easiest.
Hope this makes sense.
Thanks
Take a look at the documentation. Note that File#open receives mode "r" by default.
So the answer is: change
File.new("output.txt")
to
File.new("output.txt", "w")
Another thing you can do in Ruby is:
File.write("output.txt", reassign)
Or:
File.open("output.txt", "w")
Also, i don't know what's the purpose, but consider a big file, you might want to read batch of lines and write them to the output file each time, not read all at once to the memory.

Runtime Error using Ruby FileUtils

I've been staring at this for hours, but am not sure what i'm doing wrong. I'm trying to write a simple script to move 100 or so files from various locations in an external list. Should be simple enough, and when I run the command through irb, everything works for that one file, but when running the script I get an error. Here's my script.
#! /opt/local/bin/ruby
require 'fileutils.rb'
list_of_files = File.read "files_to_copy.txt"
source_dir = "/Volumes/data/moved_from_share/"
dest_dir = "/Volumes/data/testeroooo/"
list_of_files.each do |line|
copy_from = source_dir + line
copy_to = dest_dir + line
puts copy_from
puts copy_to
puts
FileUtils.cp_r(copy_from, copy_to)
end
Here is some example input from "files_to_copy.txt":
Accounting HG/Accounts Payable/2011/2011_06/ebi_Inv_218876.pdf
Accounting HG/Accounts Payable/2011/2011_06/expeditors_1050006142.tif
Accounting HG/Accounts Payable/2011/2011_06/expeditors_7050627938.tif
And lastly, here is my output with error:
/Volumes/data/moved_from_share/Accounting PG/Accounts Payable/2011/2011_07/
/Volumes/data/testeroooo/Accounting PG/Accounts Payable/2011/2011_07/
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:1255:in `copy': unknown file type: /Volumes/data/moved_from_share/Accounting PG/Accounts Payable/2011/2011_07/ (RuntimeError)
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:451:in `copy_entry'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:1324:in `traverse'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:448:in `copy_entry'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:423:in `cp_r'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:1395:in `fu_each_src_dest'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:1411:in `fu_each_src_dest0'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:1393:in `fu_each_src_dest'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/fileutils.rb:422:in `cp_r'
from copy_it.rb:14
from copy_it.rb:8:in `each'
from copy_it.rb:8
If you have any suggestions, I would love to hear them! Thank you!
Your file list likely contains Accounting PG/Accounts Payable/2011/2011_07/ as an entry, which is a Directory, not a File. This should work perfectly fine, as you're using cp_r.
You could override it to only copy files (assuming your file list includes the subfolder items too):
if File.file?(copy_from)
FileUtils.cp_r(copy_from, copy_to)
end

Resources