Path outside of project, Ruby Dir - ruby

I'm trying to return a list of files for a directory using the following code:
Dir[directory]
The issue I'm having is this appears to always take the url as relative to the project location. For example passing '*' into Dir returns an array containing my Gemfile etc.. I want to be able to get a list of files for directories such as /Users/jason/Pictures however when i pass in /Users/jason/Pictures/* I am returned an empty array.
Any pointers would be greatly appreciated.
Thanks.

Try:
Dir['/Users/jason/Pictures/*']
If there are files or directories embedded in that path you'll get an array back from Dir[]. If you don't, and you're sure there are files in the directory, then there's something wrong, but it's most likely in the glob string you gave it. Confirm it's the right path by moving to it and using pwd to see what the OS thinks it is. Dir[] doesn't return an error if the glob string isn't valid, it just returns an empty array.
You could try using File.exist?('glob string without *') and see whether that path exists before trying to iterate it.
On my machine, running this in IRB in my home directory:
Dir['./vim/bundle/*']
Returns:
[
[ 0] "./vim/bundle/closetag.vim",
[ 1] "./vim/bundle/ctrlp.vim",
...
[31] "./vim/bundle/vim-vividchalk",
[32] "./vim/bundle/Vundle.vim"
]
I can use an absolute path also:
Dir['/Users/ttm/vim/bundle/*']
And get:
[
[ 0] "/Users/ttm/vim/bundle/closetag.vim",
[ 1] "/Users/ttm/vim/bundle/ctrlp.vim",
...
[31] "/Users/ttm/vim/bundle/vim-vividchalk",
[32] "/Users/ttm/vim/bundle/Vundle.vim"
]
Notice that you get relative pathnames if you use a relative glob string, and absolute pathnames for an absolute glob string.

You need to use the entries method
Dir.entries('/')
=> [".", "..", ".apdisk", ".com.apple.backupd.mvlist.plist", ".dbfseventsd", ".DocumentRevisions-V100", ".DS_Store", ".file", ".fseventsd", ".hotfiles.btree", ".MobileBackups",".OSInstallMessages", ".PKInstallSandboxManager", ".Spotlight-V100", ".SymAVx86QSFile", ".Trashes", ".vol", "Applications", "bin", "cores", "dev", "etc", "home", "Library", "net", "Network", "opt", "private", "sbin", "System", "tmp", "Users", "usr", "var", "Volumes", "~"]
Here is the oficial documentation http://www.ruby-doc.org/core-2.1.2/Dir.html#method-c-entries

Related

Passing spaces in arguments for Visual Studio Pro 2019

I am trying to debug a command line program inside Visual Studio. I am sharing my configuration with another machine using Box. The paths I am passing have spaces in them and I haven't been successful in escaping the spaces so that instead of 3 arguments I get 9. This is the relevant section from the original launch.vs.json.
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "dispatcher.exe (src\\dispatcher\\dispatcher.exe)",
"name": "dispatcher.exe (src\\dispatcher\\dispatcher.exe)",
"args": [
"C:\\Users\\212434537\\Box Sync\\Edge Agent\\srasku-windows.json",
"C:\\Users\\212434537\\Box Sync\\Edge Agent\\static.json",
"C:\\Users\\212434537\\Box Sync\\Edge Agent\\dynamic.json"
]
}
None of these work.
"\"C:\\Users\\212434537\\Box Sync\\Edge Agent\\srasku-windows.json\""
"\\"C:\\Users\\212434537\\Box Sync\\Edge Agent\\srasku-windows.json\\""
"\\\"C:\\Users\\212434537\\Box Sync\\Edge Agent\\srasku-windows.json\\\""
"\\\\"C:\\Users\\212434537\\Box Sync\\Edge Agent\\srasku-windows.json\\\\""
How can I escape my spaces so that each argument is passed as a single argument instead of three. Note: I saw this question but it didn't solve my problem.
It turns out you need to surround the spaces with singly-escaped double-quotes:
Here is the resultant section:
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "dispatcher.exe (src\\dispatcher\\dispatcher.exe)",
"name": "dispatcher.exe (src\\dispatcher\\dispatcher.exe)",
"currentDir": "C:\\Users\\212434537\\source\\Edge-Agent",
"args": [
"C:\\Users\\212434537\\Box\" \"Sync\\Edge\" \"Agent\\srasku-windows.json",
"C:\\Users\\212434537\\Box\" \"Sync\\Edge\" \"Agent\\static.json",
"C:\\Users\\212434537\\Box\" \"Sync\\Edge\" \"Agent\\dynamic.json"
]
}
A simple workaround for this, is just to include the entire path in quotes, such as:
"C:\Users\212434537\Box Sync\Edge Agent\dynamic.json"
So, the escaped json becomes:
"\"C:\\Users\\212434537\\Box Sync\\Edge Agent\\dynamic.json\""

How to copy a file glob with directory structure

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

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

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