How to copy a file glob with directory structure - ruby

I want to copy selected build products, preserving their part of the directory structure, but this:
Dir.chdir('build/sources/ios_src') {
FileUtils.cp_r(Dir.glob('build/Build/Products/*/*.app*'), '/tmp/bcsh')
}
results in
Errno::ENOENT: No such file or directory # dir_s_mkdir - /tmp/bcsh/Booble.app
despite the glob returning this:
Dir.chdir('build/sources/ios_src') {
Dir.glob('build/Build/Products/*/*.app*')
}
=> ["build/Build/Products/Calabash-iphonesimulator/Booble.app",
"build/Build/Products/Calabash-iphonesimulator/Booble.app.dSYM"]
I want /tmp/bsch/build/Build/Products/.../Booble.app and .../Booble.app.dSYM not /tmp/bcsh/Booble.app and /tmp/bcsh/Booble.app.dSYM.
For clarity, I'm capable of creating a directory, but the error more usefully shows that the files would end up where I don't want them than more verbiage.

Hm. Thanks to mudasobwa for the hint about glob taking a block. This seems to copy the results of the glob, preserving both the directory structure OF the glob and within each entry:
Dir.chdir('build/sources/ios_src') {
Dir.glob('build/Build/Products/*/*.app*') { |file|
dest = File.dirname("/tmp/bcsh/#{file}")
FileUtils.mkdir_p(dest) && FileUtils.cp_r(file, dest)
}
}
Not keen on the temporary, but
.... { |file|
FileUtils.cp_r(file, FileUtils.mkdir_p(File.dirname("/tmp/bcsh/#{file}")).first)
}
is a bit extreme.

Dir.chdir('build/sources/ios_src') do
Dir.glob('build/Build/Products/*/*.app*') do |filename|
dir = File.join("/tmp/bcsh", *filename.split(File::SEPARATOR)[0...-1])
FileUtils.mkdir_p(dir)
FileUtils.cp(filename, dir)
end
end

Related

Vagrant: Ruby script to return name of last created file

We have vagrant file with trigger like
DB_NAME="mydb"
TIME=(Time.now.strftime("%Y%m%d%H%M%S"))
SQL_BACKUPS=(Dir["./config/schema/*_#{DB_NAME}.sql"])
config.trigger.before [:destroy, :provision] do |trigger|
trigger.info = "Dumping database to /vagrant/config/schema/#{TIME}_#{DB_NAME}.sql"
trigger.run_remote = {inline: "mysqldump --add-drop-table -u #{DB_USERNAME} -p#{DB_PASSWORD} #{DB_NAME} > /vagrant/config/schema/#{TIME}_#{DB_NAME}.sql"}
end
in /vagrant/config/schema/ we have backup files like:
20181116160919_mydb.sql
How to find in ruby all files like *_mydb.sql and / or return name of latest one created?
We want automatize db backup on destroy, provision & up.
EDIT:
SQL_BACKUPS=(Dir["./config/schema/*_#{DB_NAME}.sql"]).sort
SQL_BACKUPS.reverse.each do |filename|
puts "#{filename}"
end
return lists with sql files
ps, I don't have Experience with Ruby.
I know you have timestamped files but, if one doesn't have a timestamp or a way to distinguish the creation times, the method below is what I used for non-timestamped files.
File::Stat has a method ctime(returns the creation time), So it can be done like.
SQL_BACKUPS=Dir["./config/schema/*_#{DB_NAME}.sql"].map { |f| {name: f, ctime: File::Stat.new(f).ctime } }
sorted = SQL_BACKUPS.sort_by { |f| f[:ctime] }
sorted.last.name # gives the one that was created the last.

Iterative loop of files in folder with puppet

I have a set of files in a folder. I would like to pass an array of the files in a folder to some function. I saw the following example
$files= ["C:/dir/file1", "C:/dir/file2", "C:/dir/file3",
"C:/dir/file4", "C:/dir/file5"]
# function call with lambda:
$binaries.each |String $binary| {
file {"/usr/bin/$binary":
ensure => file,
}
}
but instead of declaring files manually, can I read all the files from a directory and pass it to some function?
You can use Dir to fetch all files using some pattern. For example:
[1] pry(main)> Dir["/Users/smefju/tmp/*"]
=> ["/Users/smefju/tmp/a.rb",
"/Users/smefju/tmp/asd",
"/Users/smefju/tmp/bm.rb",
"/Users/smefju/tmp/cert",
"/Users/smefju/tmp/gc",
"/Users/smefju/tmp/qq"]

Dir.glob to process all files recursively and keep track of their parent directory

I wish to process all .jpg files recursively. I need to have their parent directory available at some variable as well. So I moved from:
Dir.cwd("/some/path")
Dir.glob("**/*.jpg") { |the_file| }
to:
Dir.cwd("/some/path")
Dir.glob("**/") { |the_dir|
Dir.glob("#{the_dir}*.jpg") { |the_file|
puts "file: #{the_file} is at #{the_dir}"
}
}
Unfortunately it omits *.jpg files at the Dir.cwd itself. For my test dir:
$ find
.
./some_dir
./some_dir/another_one
./some_dir/another_one/sample_A.jpg
./some_dir/sample_S.jpg
./sample_4.jpg
./sample_1.jpg
./sample_3.jpg
./sample_2.jpg
I got output for sample_A.jpg and sample_S.jpg but not for any other.
From what I understood this should do:
Dir.glob("**/*.jpg") do |thefile|
puts "#{File.basename(thefile)} is at #{File.dirname(thefile)}"
end
dirname give you the parent directory only.
You may extend dirname by expand_path if you want the full path name.
I.e.: File.dirname(File.expand_path(thefile)) which should give you the full path to the file.
Side note, there's other methods in ruby > 2.0 from the File class, but I did stick with the basic ones here.
I found one other way with two loops which I believe can be faster in some situations as it doesn't call File.dirname for each file:
Dir.glob("{./,**/}") { |the_dir|
# puts "dir: #{the_dir}"
Dir.glob("#{the_dir}*.jpg") { |the_file|
puts "file: #{the_file} is at #{the_dir}"
}
}

No such file or directory - ruby

I am trying to read the contents of the file from a local disk as follows :
content = File.read("C:\abc.rb","r")
when I execute the rb file I get an exception as Error: No such file or directory .What am I missing in this?
In a double quoted string, "\a" is a non-printable bel character. Similar to how "\n" is a newline. (I think these originate from C)
You don't have a file with name "C:<BEL>bc.rb" which is why you get the error.
To fix, use single quotes, where these interpolations don't happen:
content = File.read('C:\abc.rb')
content = File.read("C:\/abc.rb","r")
First of all:
Try using:
Dir.glob(".")
To see what's in the directory (and therefore what directory it's looking at).
open("C:/abc.rb", "rb") { |io| a = a + io.read }
EDIT: Unless you're concatenating files together, you could write it as:
data = File.open("C:/abc.rb", "rb") { |io| io.read }

Ruby program which sorts images into different directories by their names?

I would like to make a Ruby program which sorts the images in the current directory into different subfolders, for example:
tree001.jpg, ... tree131.jpg -> to folder "tree"
apple01, ... apple20.jpg -> to folder "apple"
plum1.jpg, plum2.jpg, ... plum33.jpg -> to folder "plum"
and so on, the program should automagically recognize which files belong together by their names. I have no clue how to achive this. Till now I make a small program which collect the files with command "Dir" into an array and sort it alphabetically to help finding the appropriate classes by the file names. Does anybody have a good idea?
Check out Find:
http://www.ruby-doc.org/stdlib-2.0/libdoc/find/rdoc/Find.html
Or Dir.glob:
http://ruby-doc.org/core-2.0/Dir.html#method-c-glob
For instance:
Dir.glob("*.jpg")
will return an array that you can iterate with each.
I'd go about it something like this:
files = %w[
tree001.jpg tree03.jpg tree9.jpg
apple1.jpg apple002.jpg
plum3.jpg plum300.jpg
].shuffle
# => ["tree001.jpg", "apple1.jpg", "tree9.jpg", "plum300.jpg", "apple002.jpg", "plum3.jpg", "tree03.jpg"]
grouped_files = files.group_by{ |fn| fn[/^[a-z]+/i] }
# => {"tree"=>["tree001.jpg", "tree9.jpg", "tree03.jpg"], "apple"=>["apple1.jpg", "apple002.jpg"], "plum"=>["plum300.jpg", "plum3.jpg"]}
grouped_files.each do |grp, files|
Dir.mkdir(grp) unless Dir.exist(grp)
files.each { |f| FileUtils.mv(f, "#{grp}/#{f}") }
end
I can't test that because I don't have all the files, nor am I willing to generate them.
The important thing is group_by. It makes it easy to group the similarly named files, making it easy to walk through them.
For your case, you'll want to replace the assignment to files with Dir.glob(...) or Dir.entries(...) to get your list of files.
If you want to separate the file path from the file name, look at File.split or File.dirname and File.basename:
File.split('/path/to/foo')
=> ["/path/to", "foo"]
File.dirname('/path/to/foo')
=> "/path/to"
File.basename('/path/to/foo')
=> "foo"
Assuming every file name starts with non-digit characters followed by at least one digit character, and the initial non-digit characters define the directory you want the file moved to:
require 'fileutils'
Dir.glob("*").select{|f| File.file? f}.each do |file| # For each regular file
dir = file.match(/[^\d]*/).to_s # Determine destination directory
FileUtils.mkdir_p(dir) # Make directory if necessary
FileUtils.mv(file, dir) # Move file
end
The directories are created if necessary. You can run it again after adding files. For example, if you added the file tree1.txt later and re-ran this, it would be moved to tree/ where tree001.jpg through tree131.jpg already are.
Update: In the comments, you added the requirement that you only want to do this for files which form groups of at least 10. Here's one way to do that:
require 'fileutils'
MIN_GROUP_SIZE = 10
reg_files = Dir.glob("*").select{|f| File.file? f}
reg_files.group_by{|f| f.match(/[^\d]*/).to_s}.each do |dir, files|
next if files.size < MIN_GROUP_SIZE
FileUtils.mkdir_p(dir)
files.each do |file|
FileUtils.mv(file, dir)
end
end

Resources