From: Louis J Scoras Date: 2006-11-25T00:54:09+09:00 Subject: Re: A directory "grep" in RUBY? Peter; > Wow! Thanks, Lou. This looks interesting, but, a lot of it seems beyond > me at this point. All I want is particular files in a directory. No problem at all. If you just need it to work, you can copy that into a file, require it when you need to and do your thing. If you're interested on how it works, keep reading. If not my feelings won't be hurt =) class Dir ## # We want to replace the old Dir.glob function with one that also takes a # Regexp obeject. Now, just to come clean from the begining, this might not # be the best of ideas since running a shell glob and doing filtering # on regexes aren't quite the same thing semantically. # # That being said, pragmatically it might be useful, so here we go. First # thing that needs to be done is to move the old version of the function out # of the way. We need to do this because we're still going to use it when # the use passes in a string value representing a glob. The funky class << # self notation is because glob is a class method on Dir, not an instance # method: class << self alias_method :__original_glob, :glob end ## # Now we're free to redefine Dir.glob. Since were in class Dir, self.glob # is really the same thing. I know the method signature looks a little # funky, but it needs to match up with the original glob function. # # If you look at the rdoc for Dir.glob, you'll see that it can take a bunch # of flags, which we won't handle here; however, if the original is to keep # working, this information will need to be passed on. It's the same with # the blk parameter. The & takes the specified block and stuffs it into a # variable. This is done rather than just using yield because the block # also needs to be sent to the original method as well. def self.glob(query,*flags, &blk) ## # First off is the easy case. IF the parameter passed in is not a regex, # then we don't do anything. Just pass it off to the original function. return __original_glob(query,*flags,&blk) unless query.is_a? Regexp ## # Now, if there isn't a block to yield to, we're going a to be building up # an array with all of the matching files, so thats initialized before the # iteration starts files = [] ## # Based on the code you posted above, I assumed that you wanted the regex # just to match things in the current directory, so we'll use '.' as the # one to iterate over. If you want all the files recursively, take a look # at the Find library. Dir.new('.').each do |f| ## # Here's the check against the regex. If it doesn't match skip to the # next file in the directory. Otherwise, what happens next depends on # whether a block was passed or not... next unless query =~ f ## # If we did not get a block, just stick the matching file into the # array, if blk.nil? files << f ## # Otherwise, it yield it to the block. else blk.call(f) end end ## # Now to make it match up with the original method, nil is returned if the # block was called. Otherwise, return the array of files blk.nil?? files : nil end end ## # And that's about it. This is just showing how to use the new method, but it # won't be called if you're requiring it from another script. if $0 == __FILE__ ## # This is the block way of calling it. I works just like an iterator. Dir.glob(/\A\..*/) do |f| puts "Dot-file: #{f}" end ## # Here's without the block. It returns an array. We also do the same # search with a string just to show that the original functionality is # preserved. backups_regex = Dir.glob(/~\z/) backups_string = Dir.glob('.?*~') p backups_regex p backups_string p backups_regex == backups_string # => true end Hope that helps. -- Lou.