Ruby file full path - ruby

I am writing a program where I cycle through all files in sub-folders of a target folder and do stuff with what's writen in it. So my local folder looks like that
Folder
--Subfolder
---File
---File
---File
--Subfolder
---File
.
.
.
So I have a each loop to cycle through all subfolders and for each subfolder I am calling a method that basically do the same thing but in the subfolder and call for each file another method (parsing it a file argument which I obtained through a Dir.foreach(folder){ |file| method(file)} command).
So it looks like this :
Dir.foreach(Dir.pwd){ |folder| call_method(folder) }
def call_method(folder) Dir.foreach(folder){|file| reading_method(file) } end
That last called method (reading_method) should open called a C method and parse as an argument the full path of the file (so that the C program can open it) so I'm using File.absolute_path(file) in the reading_method but instead of returning C:/folder/subfolder/file as I want it to, it returns C:/folder/file skipping the subfolder (and thus the C program fail to execute).
Is there a way to get the full path of that file ?
Thanks for your help
EDIT : Here is the full code as asked
## Module
module GBK_Reader
PATH = "Z:/Folder/"
SAFETY = true
SAFETY_COUNT = 10
end
## Methods definitions
def read_file(file)
path = File.absolute_path(file)
c_string = `C:/Code/GBK_Reader/bin/Debug/GBK_Reader.exe #{path}`
return c_string.split(/ /).collect!{|spec| spec.to_i}
end
def read_folder(folder)
Dir.foreach(folder){ |file|
next if File.extname(file) != ".gbk"
temp = read_file(file)
#$bacteria_specs[0] += temp[0]
#$bacteria_specs[1] += temp[1]
}
return $bacteria_specs
end
## Main
# Look for folder
Dir.chdir(GBK_Reader::PATH)
puts "Directory found"
# Cycle through all sub-folders
$high_gc = {} #Hash to store high GC content bacterias
$count = 0
puts "Array variable set"
Dir.foreach(Dir.pwd){ |file|
next if file == "." || file == ".."
break if $count >= GBK_Reader::SAFETY_COUNT
$count += 1 if GBK_Reader::SAFETY
$bacteria_specs = [0.00, 0.00, 0.00]
$path = File.expand_path(file)
if File.directory?(file)
# Cycle through all .gbk files in sub-folder and call C program
read_folder(file)
else
# Call C program to directly evaluate GC content
c_string = read_file(file) if File.extname(file) == ".gbk"
$bacteria_specs[0] = c_string[0].to_i
$bacteria_specs[1] = c_string[1].to_i
end
# Evaluate GC content and store suitable entries
$bacteria_specs[2] = ($bacteria_specs[0]/$bacteria_specs[1])*100.00
$high_gc[file] = $bacteria_specs if $bacteria_specs[2] > 60
}
# Display suitable entries
puts "\n\n\n"
puts $high_gc
gets.chomp

Ok, I may have found something but it seems ugly so if anyone has a better solution by all means go ahead.
I edited my read_folder method to parse the full path to the read_file method as follow :
def read_folder(folder)
Dir.foreach(folder){ |file|
next if File.extname(file) != ".gbk"
path = File.absolute_path(folder)+'/'+File.basename(file)
temp = read_file(path)
$bacteria_specs[0] += temp[0]
$bacteria_specs[1] += temp[1]
}
return $bacteria_specs
end
And I do get the path I expect. (though my calling the C program still fails so I'll have to check somewhere else :D)

Related

How would I rewrite .sc files into a different format?

I want to export sc files from SpaceEngine, then for each file, create a new file with the same name but with the extension .txt.
Here is my code:
require 'fileutils'
require 'IO/console'
puts "Make sure your export folder is clear of everything but the files you want to turn into Object text files."
puts "Starting Process"
i = 0
Dir.foreach('C:\SpaceEngine\export') do |item|
next if item == '.' or item == '..'
i = i + 1
name = File.basename(item, ".*")
current = File.new("#{name}.txt", "w");
current.close
end
sleep 2
I have the latter part already, but I can't get it to read the original files one by one, and then only put certain things from the original into the new file.
# test.sc
# assume this is your test data
this has foo
this does not
this also has foo
this has some other stuff
this is the last line which has foo
blah
blah blah 💩
# filejunk.rb
# you need to write a method that handles the data inside the file you want to
# modify, change, replace etc. but for example
def replace_file_data(filename)
lines = File.readlines
lines.select{|l| l.include?'foo'} #assumes you only want lines with 'foo' in them
end
Dir.glob('C:\SpaceEngine\export\*.sc').each_with_index do |filename, i|
i += 1
name = File.basename(filename, ".*")
current = File.new("#{name}.txt", "w") {|f| f.write replace_file_data(filename) }
current.close
end

How do I open each file in a directory with Ruby?

I need to open each file inside a directory. My attempt at this looks like:
Dir.foreach('path/to/directory') do |filename|
next if filename == '.' || filename == '..'
puts "working on #{filename}"
# this is where it crashes
file = File.open(filename, 'r')
#some code
file.close
# more code
end
My code keeps crashing at File.open(filename, 'r'). I'm not sure what filename should be.
The filename should include the path to the file when the file is not in the same directory than the Ruby file itself:
path = 'path/to/directory'
Dir.foreach(path) do |filename|
next if filename == '.' || filename == '..'
puts "working on #{filename}"
file = File.open("#{path}/#{filename}", 'r')
#some code
file.close
# more code
end
I recommend using Find.find.
While we can use various methods from the Dir class, it will look and retrieve the list of files before returning, which can be costly if we're recursively searching multiple directories or have a huge number of files embedded in the directories.
Instead, Find.find will walk the directories, returning both the directories and files as each is found. A simple check lets us decide which we want to continue processing or whether we want to skip it. The documentation has this example which should be easy to understand:
The Find module supports the top-down traversal of a set of file paths.
For example, to total the size of all files under your home directory, ignoring anything in a “dot” directory (e.g. $HOME/.ssh):
require 'find'
total_size = 0
Find.find(ENV["HOME"]) do |path|
if FileTest.directory?(path)
if File.basename(path)[0] == ?.
Find.prune # Don't look any further into this directory.
else
next
end
else
total_size += FileTest.size(path)
end
end
I'd go for Dir.glob or File.find. But not Dir.foreach as it returns . and .. which you don't want.
Dir.glob('something/*').each do |filename|
next if File.directory?(filename)
do_something_with_the_file(filename)
end

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."

Resources