From: Robert Klemme Date: 2008-12-17T00:28:02+09:00 Subject: Re: implementing python's os.walk 2008/12/16 Brad Volz : > I seem to be having some difficulty creating a version of python's os.walk() > for ruby, and I was hoping for some pointers. > > As some background, python's os.walk() [1] is a generator function. It is > passed the top of a directory tree and it returns the following for each > subdirectory that it encounters: > . the working directory > . an Array of subdirectories > . an Array of non-directory files > > Here is some truncated output for my use case: > >>>> import os >>>> repo = '/usr/local/nfsen/profiles-data/live/lax1er1' >>>> for root, dirs, files in os.walk(repo): > ... if len(files) == 288: > ... print root > ... > /usr/local/nfsen/profiles-data/live/lax1er1/2008/11/11 > .. > /usr/local/nfsen/profiles-data/live/lax1er1/2008/12/13 > > Essentially, when there are exactly 288 files in a subdirectory, I want to > print or otherwise do something with the working directory. > The part that I think is totally wrong is: > > for o in find_dirs(d) { |a,b,c| puts "#{a} #{b} #{c}"} > > It's really only in there currently to keep the from getting > 'LocalJumpError: no block given.' Unfortunately, I have no idea what the > correct way to deal with this would be. > > The missing part would be including the directory contents in addition to > the working directory and the Array of subdirectories. > > So, I guess my main questions would be: What do I need to do to get this > sort of a generator to work? Do I need to wrap this up in a 'class' or is a > 'def' sufficient? What should the block look like, and where should it be > in the code? You have a recursive algorithm here but you want each call of the method invoke the *same block*. This can be achieved by forwarding the block with the &b notation: def find_dirs(top, &b) ... # enter recursion find_dirs(d, &b) end The way you did it, every invocation yields to the caller's block which is only the correct one for the first caller; all others yield to the block in their parent calling find_dirs. You might as well be able to create a totally different solution using Find: require 'find' roots.each do |root| dir_count = Hash.new 0 Find.find root do |file| d, f = File.split file next if /\A\.{1,2}\z/ =~ f dir_count[d] += 1 if File.file? file end dir_count.each do |dir, cnt| puts root if cnt == 288 end end Kind regards robert -- remember.guy do |as, often| as.you_can - without end