Empty directory (delete all files) - ruby

What would be a safe and efficient way to delete all files in a directory in pure Ruby? I wrote
Dir.foreach(dir_path) {|f| File.delete(f) if f != '.' && f != '..'}
but it gives me a No such file or directory error.
Thank you.

What about FileUtils.rm_rf("#{dir_path}/.", secure: true)?

FileUtils.rm_rf Dir.glob("#{dir_path}/*") if dir_path.present?

You're probably getting that error because your current working directory doesn't match dir_path -- File.delete(f) is being given just the filename for a file in dir_path. (I hope you didn't have any important files in the current working directory with same names in the dir_path directory.)
You need to use File.join(dir_path, f) to construct the filename you wish to delete. You also need to figure out how you want to handle directories:
Dir.foreach(dir_path) do |f|
fn = File.join(dir_path, f)
File.delete(fn) if f != '.' && f != '..'
end
Errno::EISDIR: Is a directory - /tmp/testing/blubber
from (irb):10:in `delete'
from (irb):10
from (irb):10:in `foreach'
from (irb):10
from :0

Everybody suggest rm_rf, but the safer way is to use rm_f, which is an alias to rm force: true.
FileUtils.rm_f Dir.glob("#{dir_path}/*")
This method will not remove directories, it will only remove files: http://ruby-doc.org/stdlib-2.2.0/libdoc/fileutils/rdoc/FileUtils.html#method-c-rm

Now days, I like using the Pathname class when working on files or directories.
This should get you started:
Pathname.new(dir_path).children.each { |p| p.unlink }
unlink will remove directories or files, so if you have nested directories you want to preserve you'll have to test for them:
Removes a file or directory, using File.unlink if self is a file, or Dir.unlink as necessary.

To tack on to MyroslavN, if you wanted to do it with Pathname (like with Rails.root, if it's a rails app) you want this:
FileUtils.rm_rf Dir.glob(Rails.root.join('foo', 'bar', '*'))
I'm posting this because I did a bad copypaste of his answer, and I accidentally wound up with an extra slash in my path:
Rails.root.join('foo', 'bar', '/*')
Which evaluates to /* because it sees it as a root path. And if you put that in the FileUtils.rm_rf Dir.glob it will attempt to recursively delete everything it can (probably limited by your permissions) in your filesystem.

The Tin Man almost got it right - but in case the directories are not empty use
Pathname.new(dir_path).children.each { |p| p.rmtree }

Here's another variation that's nice and succinct if you only have files or empty directories:
your_path.children.each.map(&:unlink)

Dir.foreach(dir_path) {|f| File.delete("#{dir_path}/#{f}") if f != '.' && f != '..'}

Related

FileUtil does not copy ".gitignore"

I used FileUtils.cp_r() to copy a whole folder. All files inside that folder is copied except .gitignore. If I change the file's name to gitignore (without period), it works fine.
I'm guessing it's because the file's name is not valid for Ruby. Is there a solution for this?
This is my code:
require "fileutils"
module MyApp
def self.create
# root of the gem dir
root = File.expand_path("..", File.dirname(__FILE__))
# "/template" is the folder that I want to copy
src_dir = File.join(root, "template")
# destination is where the command prompt opened
destination = Dir.pwd
FileUtils.cp_r( Dir["#{src_dir}/*"], destination)
end
end
I'm using Windows 8.1 Update 1. But my friend who uses Mac tested my gem and doesn't get the .gitignore too.
This is the problem:
Dir["#{src_dir}/*"]
Globbing does not include filenames starting with ..
Use other methods like this instead:
sources = Dir.entries("#{src_dir}/").reject{ |e| e == '.' || e == '..' }.map{ |e| "#{src_dir}/#{e}" }
FileUtils.cp_r(sources, destination)
You can also use File::FNM_DOTMATCH:
Dir.glob("#{src_dir}/*", File::FNM_DOTMATCH)

How do I create directory if none exists using File class in Ruby?

I have this statement:
File.open(some_path, 'w+') { |f| f.write(builder.to_html) }
Where
some_path = "somedir/some_subdir/some-file.html"
What I want to happen is, if there is no directory called somedir or some_subdir or both in the path, I want it to automagically create it.
How can I do that?
You can use FileUtils to recursively create parent directories, if they are not already present:
require 'fileutils'
dirname = File.dirname(some_path)
unless File.directory?(dirname)
FileUtils.mkdir_p(dirname)
end
Edit: Here is a solution using the core libraries only (reimplementing the wheel, not recommended)
dirname = File.dirname(some_path)
tokens = dirname.split(/[\/\\]/) # don't forget the backslash for Windows! And to escape both "\" and "/"
1.upto(tokens.size) do |n|
dir = tokens[0...n]
Dir.mkdir(dir) unless Dir.exist?(dir)
end
For those looking for a way to create a directory if it doesn't exist, here's the simple solution:
require 'fileutils'
FileUtils.mkdir_p 'dir_name'
Based on Eureka's comment.
directory_name = "name"
Dir.mkdir(directory_name) unless File.exists?(directory_name)
How about using Pathname?
require 'pathname'
some_path = Pathname("somedir/some_subdir/some-file.html")
some_path.dirname.mkdir_p
some_path.write(builder.to_html)
Based on others answers, nothing happened (didn't work). There was no error, and no directory created.
Here's what I needed to do:
require 'fileutils'
response = FileUtils.mkdir_p('dir_name')
I needed to create a variable to catch the response that FileUtils.mkdir_p('dir_name') sends back... then everything worked like a charm!
Along similar lines (and depending on your structure), this is how we solved where to store screenshots:
In our env setup (env.rb)
screenshotfolder = "./screenshots/#{Time.new.strftime("%Y%m%d%H%M%S")}"
unless File.directory?(screenshotfolder)
FileUtils.mkdir_p(screenshotfolder)
end
Before do
#screenshotfolder = screenshotfolder
...
end
And in our hooks.rb
screenshotName = "#{#screenshotfolder}/failed-#{scenario_object.title.gsub(/\s+/,"_")}-#{Time.new.strftime("%Y%m%d%H%M%S")}_screenshot.png";
#browser.take_screenshot(screenshotName) if scenario.failed?
embed(screenshotName, "image/png", "SCREENSHOT") if scenario.failed?
The top answer's "core library" only solution was incomplete. If you want to only use core libraries, use the following:
target_dir = ""
Dir.glob("/#{File.join("**", "path/to/parent_of_some_dir")}") do |folder|
target_dir = "#{File.expand_path(folder)}/somedir/some_subdir/"
end
# Splits name into pieces
tokens = target_dir.split(/\//)
# Start at '/'
new_dir = '/'
# Iterate over array of directory names
1.upto(tokens.size - 1) do |n|
# Builds directory path one folder at a time from top to bottom
unless n == (tokens.size - 1)
new_dir << "#{tokens[n].to_s}/" # All folders except innermost folder
else
new_dir << "#{tokens[n].to_s}" # Innermost folder
end
# Creates directory as long as it doesn't already exist
Dir.mkdir(new_dir) unless Dir.exist?(new_dir)
end
I needed this solution because FileUtils' dependency gem rmagick prevented my Rails app from deploying on Amazon Web Services since rmagick depends on the package libmagickwand-dev (Ubuntu) / imagemagick (OSX) to work properly.

Search in current dir only

Im using
Find.find("c:\\test")
to search for files in a dir. I just want to search the dir at this level though, so any dir within c:\test does not get searched.
Is there another method I can use ?
Thanks
# Temporarily make c:\test your current directory
Dir.chdir('c:/test') do
# Get a list of file names just in this directory as an array of strings
Dir['*'].each do |filename|
# ...
end
end
Alternatively:
# Get a list of paths like "c:/test/foo.txt"
Dir['c:/test/*'] do |absolute|
# Get just the filename, e.g. "foo.txt"
filename = File.basename(absolute)
# ...
end
With both you can get just the filenames into an array, if you like:
files = Dir.chdir('c:/text'){ Dir['*'] }
files = Dir['c:/text/*'].map{ |f| File.basename(f) }
Find's prune method allows you to skip a current file or directory:
Skips the current file or directory,
restarting the loop with the next
entry. If the current file is a
directory, that directory will not be
recursively entered. Meaningful only
within the block associated with
Find::find.
Find.find("c:\\test") do |path|
if FileTest.directory?(path)
Find.prune # Don't look any further into this directory.
else
# path is not a directory, so must be file under c:\\test
# do something with file
end
end
You may use Dir.foreach(), for example, to list all the files under c:\test
Dir.foreach("c:\\test") {|x| puts "#{x}" }

How can you find the most recently modified folder in a directory using Ruby?

How can you find the most recently modified folder (NOT A FILE) in a directory using Ruby?
Dir.glob("a_directory/*/").max_by {|f| File.mtime(f)}
Dir.glob("a_directory/*/") returns all the directory names in a_directory (as strings) and max_by returns the name of the directory for which File.mtime returns the greatest (i.e. most recent) date.
Edit: updated answer to match the updated question
Find the most recently modified directory in the current directory:
folders = Dir["*"].delete_if{|entry| entry.include? "."}
newest = folders[0]
folders.each{|folder| newest = folder if File.mtime(folder) > File.mtime(newest)}
Expanding off of sepp2k's answer a bit to add recursively checking all subdirectories for those coming across this:
#!/usr/bin/env ruby
if ARGV.count != 1 then raise RuntimeError, "Usage: newest.rb '/path/to/your dir'" end
Dir.chdir(ARGV[0])
newest_file = Dir.glob("**/").max_by {|f| File.mtime(f)}
if newest_file != nil then
puts newest_file.to_s + " " + File.mtime(newest_file).to_s
else
puts "No subdirectories"
end
and use this instead if you want all files and not just directories:
Dir.glob("**/*")

Getting a list of folders in a directory

How do I get a list of the folders that exist in a certain directory with ruby?
Dir.entries() looks close but I don't know how to limit to folders only.
I've found this more useful and easy to use:
Dir.chdir('/destination_directory')
Dir.glob('*').select {|f| File.directory? f}
it gets all folders in the current directory, excluded . and ...
To recurse folders simply use ** in place of *.
The Dir.glob line can also be passed to Dir.chdir as a block:
Dir.chdir('/destination directory') do
Dir.glob('*').select { |f| File.directory? f }
end
Jordan is close, but Dir.entries doesn't return the full path that File.directory? expects. Try this:
Dir.entries('/your_dir').select {|entry| File.directory? File.join('/your_dir',entry) and !(entry =='.' || entry == '..') }
In my opinion Pathname is much better suited for filenames than plain strings.
require "pathname"
Pathname.new(directory_name).children.select { |c| c.directory? }
This gives you an array of all directories in that directory as Pathname objects.
If you want to have strings
Pathname.new(directory_name).children.select { |c| c.directory? }.collect { |p| p.to_s }
If directory_name was absolute, these strings are absolute too.
Recursively find all folders under a certain directory:
Dir.glob 'certain_directory/**/*/'
Non-recursively version:
Dir.glob 'certain_directory/*/'
Note: Dir.[] works like Dir.glob.
With this one, you can get the array of a full path to your directories, subdirectories, subsubdirectories in a recursive way.
I used that code to eager load these files inside config/application file.
Dir.glob("path/to/your/dir/**/*").select { |entry| File.directory? entry }
In addition we don't need deal with the boring . and .. anymore. The accepted answer needed to deal with them.
directory = 'Folder'
puts Dir.entries(directory).select { |file| File.directory? File.join(directory, file)}
You can use File.directory? from the FileTest module to find out if a file is a directory. Combining this with Dir.entries makes for a nice one(ish)-liner:
directory = 'some_dir'
Dir.entries(directory).select { |file| File.directory?(File.join(directory, file)) }
Edit: Updated per ScottD's correction.
Dir.glob('/your_dir').reject {|e| !File.directory?(e)}
$dir_target = "/Users/david/Movies/Camtasia 2/AzureMobileServices.cmproj/media"
Dir.glob("#{$dir_target}/**/*").each do |f|
if File.directory?(f)
puts "#{f}\n"
end
end
For a generic solution you probably want to use
Dir.glob(File.expand_path(path))
This will work with paths like ~/*/ (all folders within your home directory).
We can combine Borh's answer and johannes' answer to get quite an elegant solution to getting the directory names in a folder.
# user globbing to get a list of directories for a path
base_dir_path = ''
directory_paths = Dir.glob(File.join(base_dir_path, '*', ''))
# or recursive version:
directory_paths = Dir.glob(File.join(base_dir_path, '**', '*', ''))
# cast to Pathname
directories = directory_paths.collect {|path| Pathname.new(path) }
# return the basename of the directories
directory_names = directories.collect {|dir| dir.basename.to_s }
Only folders ('.' and '..' are excluded):
Dir.glob(File.join(path, "*", File::SEPARATOR))
Folders and files:
Dir.glob(File.join(path, "*"))
I think you can test each file to see if it is a directory with FileTest.directory? (file_name). See the documentation for FileTest for more info.

Resources