How can I copy files and rename them according to their origin directories using Ruby? - 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.

Related

Can't open a file in docx gem when I call it through a string

I am using the docx gem to read a docx file. The code works when I write it like this:
require 'docx'
doc = Docx::Document.open('example.docx')
puts doc
It prints the doc perfectly. However, I need to get the path from the user through a gets. I need to do this:
require docx
puts "Provide the path of the document:"
document_path = gets.chomp.tr(" ", "") #it makes sure that any accidental whitespace is removed.
doc = Docx::Document.open(document_path)
puts doc
With this code I expect to get the same result that with the former one. The only difference is that I call the docx document to open through a string, not explicitly. Instead, I get this error:
/var/lib/gems/2.3.0/gems/rubyzip-1.1.7/lib/zip/file.rb:82:in `initialize': File '/root/Documents/Projects/Wordsworth/example.docx' (Zip::Error)
not found
from /var/lib/gems/2.3.0/gems/rubyzip-1.1.7/lib/zip/file.rb:96:in `new'
from /var/lib/gems/2.3.0/gems/rubyzip-1.1.7/lib/zip/file.rb:96:in `open'
from /var/lib/gems/2.3.0/gems/docx-0.2.07/lib/docx/document.rb:25:in `initialize'
from /var/lib/gems/2.3.0/gems/docx-0.2.07/lib/docx/document.rb:50:in `new'
from /var/lib/gems/2.3.0/gems/docx-0.2.07/lib/docx/document.rb:50:in `open'
from test.rb:17:in `<main>'
I visited the docx gem github page but in the examples it gives the docx called is always explicitly written by the coder, never a string. I hope I can get some help. Thanks a lot!

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

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

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.

How to make a dir in Ruby

So I'm making this project in Ruby, and I copied this code from somewhere and It's not working.
Code:
dirname = File.dirname("C:/ProgramFiles/RubyLists")
require 'fileutils'
unless File.directory?(dirname)
File.mkdir(dirname)
end #This block will make the directory.
print("Mk. Worked.")
Error:
C:/Users/User/RubymineProjects/rubylists/main.rb:6:in `<top (required)>': undefined method `makedir' for File:Class (NoMethodError)
from -e:1:in `load'
from -e:1:in `<main>'
If you need anymore info let me know and I will provide it if I can. Thanks!
FileUtils::mkdir exist, not File::mkdir.
Thus change File.mkdir(dirname) to FileUtils.mkdir(dirname).
Write your code :-
dirname = "C:/ProgramFiles/RubyLists"
require 'fileutils'
unless Dir.exist?(dirname)
FileUtils.mkdir(dirname)
end #This block will make the directory.
print("Mk. Worked.")
Since you're using FileUtils you can use mkdir
FileUtils.mkdir("a/b/c")
though if any of the parent folders don't exist it would just crash. I typically use mkdir_p since it recursively creates directories as needed (unless I want it to crash, for example if the folder names were wrong)
# function for create folder
def createFolder(folderName)
#folderName=folderName
if File.directory?(#folderName)
return "The Folder "+#folderName+" already exist"
else
Dir.mkdir(#folderName,0700)
return "Created"
end
end
to call it just type
createFolder('folderName')

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