Listing a Directory

Problem

You want to list or process the files or subdirectories within a directory.

Solution

If you're starting from a directory name, you can use Dir.entries to get an array of the items in the directory, or Dir.foreach to iterate over the items. Here's an exampleof each run on a sample directory:

	# See the chapter intro to get the create_tree library
	require 'create_tree'
	create_tree 'mydir' =>
	 [ {'subdirectory' => [['file_in_subdirectory', 'Just a simple file.']] },
	 '.hidden_file', 'ruby_script.rb', 'text_file' ]
	
	 
Dir.entries('mydir')
	
	# => [".", "..", ".hidden_file", "ruby_script.rb", "subdirectory",
	# "text_file"]
	
	 
Dir.foreach('mydir') { |x| puts x if x != "." && x != ".."}
	# .hidden_file
	# ruby_script.rb
	# subdirectory
	# text_file

You can also use Dir[] to pick up all files matching a certain pattern, using a format similar to the bash shell's glob format (and somewhat less similar to the wildcard format used by the Windows command-line shell):

	# Find all the "regular" files and subdirectories in mydir. This excludes
	# hidden files, and the special directories . and ..
	Dir["mydir/*"]
	# => ["mydir/ruby_script.rb", "mydir/subdirectory", "mydir/text_file"]
	
	# Find all the .rb files in mydir
	Dir["mydir/*.rb"] # => ["mydir/ruby_script.rb"]

You can also open a directory handle with Dir#open, and treat it like any other Enumerable. Methods like each,each_with_index, grep, and reject will all work (but see below if you want to call them more than once). As with File#open, you should do your directory processing in a code block so that the directory handle will get closed once you're done with it.

	Dir.open('mydir') { |d| d.grep /file/ }
	# => [".hidden_file", "text_file"]
	
	Dir.open('mydir') { |d| d.each { |x| puts x } }
	# .
	# ..
	# .hidden_file
	# ruby_script.rb
	# subdirectory
	# text_file

 

Discussion

Reading entries from a Dir object is more like reading data from a file than iterating over an array. If you call one of the Dir instance methods and then want to call another one on the same Dir object, you'll need to call Dir#rewind first to go back to the beginning of the directory listing:

	#Get all contents other than ".", "..", and hidden files.

	d = Dir.open('mydir')
	d.reject { |f| f[0] == '.' }
	# => ["subdirectory", "ruby_script.rb", "text_file"]
	#Now the Dir object is useless until we call Dir#rewind.
	d.entries.size # => 0
	d.rewind
	d.entries.size # => 6

	#Get the names of all files in the directory.
	d.rewind
	d.reject { |f| !File.file? File.join(d.path, f) }
	# => [".hidden_file", "ruby_script.rb", "text_file"]
	
	d.close

Methods for listing directories and looking for files return string pathnames instead of File and Dir objects. This is partly for efficiency, and partly because creating a File or Dir actually opens up a filehandle on that file or directory.

Even so, it's annoying to have to take the output of these methods and patch together real File or Dir objects on which you can operate. Here's a simple method that will build a File or Dir, given a filename and the name or Dir of the parent directory:

	def File.from_dir(dir, name)
	 dir = dir.path if dir.is_a? Dir
	 path = File.join(dir, name)
	 (File.directory?(path) ? Dir : File).open(path) { |f| yield f }
	end

As with File#open and Dir#open, the actual processing happens within a code block:

	File.from_dir("mydir", "subdirectory") do |subdir|
	 File.from_dir(subdir, "file_in_subdirectory") do |file|
	 puts %{My path is #{file.path} and my contents are "#{file.read}".}
	 end
	end
	# My path is mydir/subdirectory/file_in_subdirectory and my contents are
	# "Just a simple file".

Globs make excellent shortcuts for finding files in a directory or a directory tree. Especially useful is the ** glob, which matches any number of directories. A glob is the easiest and fastest way to recursively process every file in a directory tree, although it loads all the filenames into an array in memory. For a less memoryintensive solution, see the find library, described in Recipe 6.12.

	Dir["mydir/**/*"]
	# => ["mydir/ruby_script.rb", "mydir/subdirectory", "mydir/text_file",
	# "mydir/subdirectory/file_in_subdirectory"]
	
	Dir["mydir/**/*file*"]
	# => ["mydir/text_file", "mydir/subdirectory/file_in_subdirectory"]

A brief tour of the other features of globs:

	#Regex-style character classes
	Dir["mydir/[rs]*"] # => ["mydir/ruby_script.rb", "mydir/subdirectory"]
	Dir["mydir/[^s]*"] # => ["mydir/ruby_script.rb", "mydir/text_file"]

	# Match any of the given strings
	Dir["mydir/{text,ruby}*"] # => ["mydir/text_file", "mydir/ruby_script.rb"]

	# Single-character wildcards
	Dir["mydir/?ub*"] # => ["mydir/ruby_script.rb", "mydir/subdirectory"]

Globs will not pick up files or directories whose names start with periods, unless you match them explicitly:

	Dir["mydir/.*"] # => ["mydir/.", "mydir/..", "mydir/.hidden_file"]

 

See Also

  • Recipe 6.12, "Walking a Directory Tree"
  • Recipe 6.20, "Finding the Files You Want"


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net