Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Directory Traversal in Ruby

Tags:

ruby

I have been trying to implement a directory traversal in Ruby for part of a bigger program using the simple recursive approach. However I have found that Dir.foreach does not include the directories inside of it. How can I get them listed?

Code:

def walk(start)
  Dir.foreach(start) do |x|
    if x == "." or x == ".."
      next
    elsif File.directory?(x)
      walk(x)
    else
      puts x
    end
  end
end
like image 431
squiguy Avatar asked Mar 29 '13 05:03

squiguy


2 Answers

The problem is that each time you recurse, the path you pass to File.directory? is no is just the entity (file or directory) name; all context is lost. So say you go into one/two/three/ to check if one/two/three/file.txt is a directory, File.directory? just gets "file.txt" as the path instead of the whole thing, from the perspective of the top-level directory. You have to maintain the relative path each time you recurse. This seems to work fine:

def walk(start)
  Dir.foreach(start) do |x|
    path = File.join(start, x)
    if x == "." or x == ".."
      next
    elsif File.directory?(path)
      puts path + "/" # remove this line if you want; just prints directories
      walk(path)
    else
      puts x
    end
  end
end
like image 156
Jorge Israel Peña Avatar answered Oct 26 '22 17:10

Jorge Israel Peña


For recursion you should use Find:

From the documentation:

The Find module supports the top-down traversal of a set of file paths.

For example, to total the size of all files under your home directory, ignoring anything in a “dot” directory (e.g. $HOME/.ssh):

require 'find'

total_size = 0

Find.find(ENV["HOME"]) do |path|
  if FileTest.directory?(path)
    if File.basename(path)[0] == ?.
      Find.prune       # Don't look any further into this directory.
    else
      next
    end
  else
    total_size += FileTest.size(path)
  end
end
like image 16
the Tin Man Avatar answered Oct 26 '22 18:10

the Tin Man