Check if some files in array exist in directory - ruby

I wrote the following code to rename some files in a directory (from 'rb' to 'origin'):
original_files = ['test1.rb', 'test2.rb']
ruby_block "Rename file" do
block do
for filename in original_files
newname = filename.split(".")[0] + '.origin'
::File.rename("C:\\Test\\#{filename}", "C:\\Test\\#{newname}")
end
end
end
When I run it for the second time, I get an error that these files don't exist, which is expected.
How can I check if these files exist or not like this:
if ::File.exist?("C:\\Test\\*.origin")
Chef::Log.info("########## Your files are already renamed ############")
else
my_code
end
or in another way (maybe to check it in loop)?

This is my solution after numerous attempts:
origin_files = ['test1.rb', 'test2.rb']
dir_path = "C:\\Test"
ruby_block "Rename file" do
block do
for filename in origin_files
newname = filename.split(".")[0] + '.origin'
if ::File.exist?("#{dir_path}\\#{newname}")
Chef::Log.info("### Your file: #{newname} already renamed ###")
else
::File.rename("#{dir_path}\\#{filename}", "#{dir_path}\\#{newname}")
end
end
end
end

you can check this files like this code
files = ["test1.rb", "test2.rb"]
path = "C:/test"
files.each { |file_name|
base_name = File.basename(file_name, ".rb")
new_name = base_name + ".origin"
if File.exists?(File.join(path, new_name))
Chef::Log.info("########## Your file #{base_name} are already renamed ############")
elsif File.exists?(File.join(path, file_name))
File.rename(File.join(path, file_name), File.join(path, new_name))
Chef::Log.info("########## Your file #{base_name} was renamed ############")
else
Chef::Log.info("########## Your file #{file_name} not exist ############")
end
}

You can use Chef guard for this purpose. It automatically logs the event during the chef-client run as
ruby_block xxx (skipped due to not_if)
Here is the example:
origin_files = ['test1.rb', 'test2.rb']
dir_path = "C:\\Test"
origin_files.each do | old_file |
base_name = File.basename(old_file, ".rb")
newfile = base_name + ".origin"
ruby_block "rename #{old_file}" do
block do
File.rename(File.join(dir_path, old_file), File.join(dir_path, newfile))
end
not_if { File.exist?("#{dir_path}\\#{newfile}") }
end
end

Related

How do I copy .htaccess files using Rake?

I am in the process of creating some build scripts, using Rake, that will be used as part of the overall process of deploying our web services to the cloud via Docker containers. In order to accomplish this we combine resources from several repos using Rake to "assemble" the directory/file layout. This all work well save for one item, .htaccess files.
Here is the copy function that I've created:
require 'fileutils'
EXT_ALLOWED = ["html", "css", "js", "svg", "otf", "eot", "ttf", "woff", "jpeg", "map", "ico", "map", "png", "db", "php", "conf"]
def copy_to(dest, src, trim="")
files = FileList.new()
EXT_ALLOWED.each {|ext| files.include "#{src}/**/*.#{ext}"}
files.each do |file|
dir = File.dirname(file)
filename = File.basename(file)
trimming = "/shared/" + trim + "(.*)"
path = dir.match(trimming)
if path == nil || dest == path[1] + '/'
bin = dest
else
bin = File.join(dest, path[1] + '/')
end
puts "copying #{file} to #{bin}"
FileUtils.mkdir_p(bin)
FileUtils.cp file, bin
end
end
The usage for this would be:
desc 'copies from shared/admin to the base server directory'
task :admin do
# Copy admin over
dest = 'www-server/'
src = '../shared/admin'
trim = "admin/"
copy_to dest, src, trim
end
The trim variable is there to make sure files are copied to the appropriate directories. In this case files in admin are copied directly to www-server without an admin subdirectory.
I, naively, tried adding "htaccess" to the EXT_ALLOWED array, but that failed.
I have also followed some items online, but most have to do with Octopress which does not solve the problem.
The .htaccess file is in ../shared/admin and needs to end up in www-server/, can I make that happen within this function? Or do I need to write something specifically for file names beginning with dots?
In this case, looking for a quick and dirty (yes...I feel dirty doing it this way!) option, I wrote a function which specifically looks for the .htaccess file in a particular directory:
def copy_htaccess(src, dest)
files = Dir.glob("#{src}/.*")
files.each do |file|
filename = File.basename(file)
if filename == ".htaccess"
puts "copying #{file} to #{dest}"
FileUtils.mkdir_p(dest)
FileUtils.cp file, dest
end
end
end
With the usage being performed this way:
desc 'copies the .htaccess file from one root to the web root'
task :htaccess do
src = '../shared/admin'
dest = 'www-server/'
copy_htaccess src, dest
end
Here I am able to use Dir.glob() to list all files starting with a ., then test for the .htaccess file and perform the copying.
I will be looking into ways to modifying the single copy function to make this cleaner, if possible. Perhaps this can be done by globbing the directory and adding the files starting with . to the files array.
EDIT: Rather than creating an additional function I found that I could just push the .htaccess file's information onto the end of the files array in the original copying function, after first checking if it exists in the source directory:
if File.file?("#{src}/.htaccess")
files.push("#{src}/.htaccess")
end
Making the whole function as shown below:
def copy_to(dest, src, trim="")
files = FileList.new()
EXT_ALLOWED.each {|ext| files.include "#{src}/**/*.#{ext}"}
if File.file?("#{src}/.htaccess")
files.push("#{src}/.htaccess")
end
files.each do |file|
dir = File.dirname(file)
filename = File.basename(file)
trimming = "/shared/" + trim + "(.*)"
path = dir.match(trimming)
if path == nil || dest == path[1] + '/'
bin = dest
else
bin = File.join(dest, path[1] + '/')
end
puts "copying #{file} to #{bin}"
FileUtils.mkdir_p(bin)
FileUtils.cp file, bin
end
end
Note that I am using .file? to test for an actual file where .exists? can return a directories truthiness. In the end you can use either method depending on your situation.

Directory walk call method when directory is reached

Trying to write a script that will search through a directory and sub-directories for specific files. I would like to do know how a certain directory or directories come up to call a method.
this is what I have tried and failed:
def display_directory(path)
list = Dir[path+'/*']
return if list.length == 0
list.each do |f|
if File.directory? f #is it a directory?
if File.directory?('config')
puts "this is the config folder"
end
printf "%-50s %s\n", f, "is a directory:".upcase.rjust(25)
else
printf "%-50s %s\n", f, "is not a directory:".upcase.rjust(25)
end
end
end
start = File.join("**")
puts "Processing directory\n\n".upcase.center(30)
display_directory start
this is what I want to happen.
app
app/controllers
app/helpers
app/mailers
app/models
app/models/bugzilla
app/models/security
app/views
app/views/auth
app/views/calendar
app/views/layouts
app/views/step
app/views/step_mailer
app/views/suggestion
app/views/suggestion_mailer
app/views/task
app/views/user
bin
--------------------------------------
config <----------(call method foo)
config/environments
config/initializers
config/locales
--------------------------------------
db
db/bugzilla
db/migrate
db/security
lib
lib/tasks
log
public
public/images
public/javascripts
public/stylesheets
script
script/performance
script/process
--------------------------
test <---------(call method foobar)
test/fixtures
test/fixtures/mailer
test/functional
test/integration
test/performance
test/unit
--------------------------
vendor
vendor/plugins
Instead
if File.directory?('config')
Try
if f.path.include?('config')
but this will work for every directory that have config on the name. You can put a larger substring to make a better match.
Also, it is very idiomatic in ruby use do..end for multiline blocks and {..} for single line.
I figured out a way. this works pretty well. I've added a method to show all the files in mentioned directory when reached.
def special_dir(path)
puts "------------------------------------"
sp_path = Dir.glob(File.join(path,"*","**"))
sp_path.each do |cf|
puts "\t" + cf
end
end
def walk(path)
list = Dir[path+'/*'].reject{ |r| r['doc'] || r['tmp']}
list.each do |x|
path = File.join(path, x)
if File.directory?(x)
if x =~ /config/ or x =~ /test/
special_dir(x)
else
puts "#{x}"
walk(path)
end
else
#puts x
end
end
end
start = File.join("**")
walk start

Script to append files

I am trying to write a script to do the following:
There are two directories A and B. In directory A, there are files called "today" and "today1". In directory B, there are three files called "today", "today1" and "otherfile".
I want to loop over the files in directory A and append the files that have similar names in directory B to the files in Directory A.
I wrote the method below to handle this but I am not sure if this is on track or if there is a more straightforward way to handle such a case?
Please note I am running the script from directory B.
def append_data_to_daily_files
directory = "B"
Dir.entries('B').each do |file|
fileName = file
next if file == '.' or file == '..'
File.open(File.join(directory, file), 'a') {|file|
Dir.entries('.').each do |item|
next if !(item.match(/fileName/))
File.open(item, "r")
file<<item
item.close
end
#file.puts "hello"
file.close
}
end
end
In my opinion, your append_data_to_daily_files() method is trying to do too many things -- which makes it difficult to reason about. Break down the logic into very small steps, and write a simple method for each step. Here's a start along that path.
require 'set'
def dir_entries(dir)
Dir.chdir(dir) {
return Dir.glob('*').to_set
}
end
def append_file_content(target, source)
File.open(target, 'a') { |fh|
fh.write(IO.read(source))
}
end
def append_common_files(target_dir, source_dir)
ts = dir_entries(target_dir)
ss = dir_entries(source_dir)
common_files = ts.intersection(ss)
common_files.each do |file_name|
t = File.join(target_dir, file_name)
s = File.join(source_dir, file_name)
append_file_content(t, s)
end
end
# Run script like this:
# ruby my_script.rb A B
append_common_files(*ARGV)
By using a Set, you can easily figure out the common files. By using glob you can avoid the hassle of filtering out the dot-directories. By designing the code to take its directory names from the command line (rather than hard-coding the names in the script), you end up with a potentially re-usable tool.
My solution....
def append_old_logs_to_daily_files
directory = "B"
#For each file in the folder "B"
Dir.entries('B').each do |file|
fileName = file
#skip dot directories
next if file == '.' or file == '..'
#Open each file
File.open(File.join(directory, file), 'a') {|file|
#Get each log file from the current directory in turn
Dir.entries('.').each do |item|
next if item == '.' or item == '..'
#that matches the day we are looking for
next if !(item.match(fileName))
#Read the log file
logFilesToBeCopied = File.open(item, "r")
contents = logFilesToBeCopied.read
file<<contents
end
file.close
}
end
end

When to use a new variable vs string interpolation?

I wrote a script that I decided to refactor so I could add functionality to it as my coworkers think of it. I only saved four lines in the effort, but the main change is I removed both methods and reduced the number of called variables in favor of string interpolation/manipulation. Is there a preference for this? Is it better to declare a new variable just to use once, or is it more DRY to just make minor tweaks to the string when you need to use it? For example here is the original code:
def validate_directory(dir)
puts "Enter the full directory path of the flv files." unless dir
input = dir || gets.chomp
input.gsub!('\\', '/')
input += '/' unless input[-1..-1] == '/'
until File.directory?(input) && Dir.glob("#{input}*.flv") != []
puts "That directory either doesn't exist or contains no .flv files. \nEnter the full directory path of the flv files."
input = $stdin.gets.chomp
input.gsub!('\\', '/')
input += '/' unless input[-1..-1] == '/'
end
dir = input
end
def output(flv, location)
title = flv.dup.gsub!(".flv", ".html")
vid = flv.dup
vid.slice!(0..6)
body = $EMBED.gsub("sample.flv", vid)
htmlOutput = File.open(title, "w")
htmlOutput.write(body)
htmlOutput.close
linkList = File.open("#{location}List of Links.txt", "a")
linkList.write($BASE + vid.gsub(".flv", ".html") + "\n")
linkList.close
puts "Files created successfully."
end
dir = ARGV[0].dup unless ARGV.empty?
folder = validate_directory(dir)
files = folder.clone + "*.flv"
flvs = Dir.glob("#{files}")
File.delete("#{folder}List of Links.txt") if File.exists?("#{folder}List of Links.txt")
flvs.each { |flv| output(flv, folder) }
And the new stuff:
flash_folder = ARGV[0].dup unless ARGV.empty?
if !flash_folder
puts "Enter the full directory path of the flv files."
flash_folder = gets.chomp
end
flash_folder.gsub!('\\', '/')
flash_folder += '/' unless flash_folder[-1..-1] == '/'
until File.directory?(flash_folder) && Dir.glob("#{flash_folder}*.flv") != []
puts "That directory either doesn't exist or contains no .flv files. \nEnter the full directory path of the flv files."
flash_folder = $stdin.gets.chomp
flash_folder.gsub!('\\', '/')
flash_folder += '/' unless flash_folder[-1..-1] == '/'
end
flash_files = Dir.glob("#{flash_folder}*.flv")
File.delete("#{flash_folder}List of Links.txt") if File.exists?("#{flash_folder}List of Links.txt")
flash_files.each do |flv|
html_output = File.open("#{flv.gsub(".flv", ".html")}", "w")
html_output.write("#{embed_code.gsub("sample.flv", flv.slice(7..flv.length))}")
html_output.close
link_list = File.open("#{flash_folder}List of Links.txt", "a")
link_list.write("#{flash_url}#{flv.slice(2..flv.length).gsub(".flv", ".html")}\n")
link_list.close
end
puts "Finished."

How to get a list of files in a directory with Ruby?

Here's what I have so far:
class FileRenamer
def RenameFiles(folder_path)
baseDirectory = folder_path
files = Dir.glob("*")
end
end
puts "Renaming files..."
renamer = FileRenamer.new()
files = renamer.RenameFiles("/home/papuccino1/Desktop/Test")
puts files
puts "Renaming complete."
The problem is that it seems to be getting files on the directory where my .rb file is running from.
How can I set the directory to where I want it to be? Notice I have the baseDirectory variable there.
files = Dir.glob(File.join(folder_path, "*"))
files = Dir.glob(folder_path + '/*')
...
Dir.chdir(baseDirectory)
files = Dir.glob("*")
...
Btw, using CamelCase for variables and methods in ruby is not good (it's only for modules and classes). Use snake_case for it.
If you just want files
class FileRenamer
def RenameFiles(folder_path)
files = Dir.glob( File.join(".","*")).select{|x| test(?f,x)}
end
end
Find.find(#path) do |path|
if FileTest.directory?(path)
#dirs.push(path)
else
#files.push(path)
end
end

Resources