mirror of
https://github.com/ruby/ruby.git
synced 2026-08-02 20:49:12 +08:00
114 lines
3.2 KiB
Ruby
114 lines
3.2 KiB
Ruby
# frozen_string_literal: true
|
|
#
|
|
# find.rb: the Find module for processing all files under a given directory.
|
|
#
|
|
|
|
# :markup: markdown
|
|
#
|
|
# \Module \Find supports the top-down traversal of entries in the file system.
|
|
module Find
|
|
|
|
# The version string
|
|
VERSION = "0.2.0"
|
|
|
|
# :markup: markdown
|
|
#
|
|
# With a block given, performs a depth-first traversal of each given path in `paths`;
|
|
# calls the block with each found file or directory path:
|
|
#
|
|
# ```ruby
|
|
# paths = []
|
|
# Find.find('bin', 'jit') {|path| paths << path }
|
|
# paths
|
|
# # =>
|
|
# # ["bin",
|
|
# # "bin/gem",
|
|
# # "jit",
|
|
# # "jit/Cargo.toml",
|
|
# # "jit/src",
|
|
# # "jit/src/lib.rs"]
|
|
# ```
|
|
#
|
|
# Raises an exception if a given path cannot be read.
|
|
#
|
|
# When keyword argument `ignore_error` is given as `true` (the default),
|
|
# certain exceptions during traversal are ignored (i.e., silently rescued):
|
|
# Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG, Errno::EINVAL;
|
|
# when given as `false`, no exceptions are rescued.
|
|
#
|
|
# Note that these exceptions may be ignored only in `Find` traversal code;
|
|
# an exception raised before traversal begins,
|
|
# or raised while in the block is not ignored.
|
|
# Each of the calls below raises an Errno::ENOENT exception that is not ignored:
|
|
#
|
|
# ```ruby
|
|
# Find.find('nosuch') { }
|
|
# Find.find('lib') {|entry| raise Errno::ENOENT }
|
|
# ```
|
|
#
|
|
# With no block given, returns a new Enumerator.
|
|
def find(*paths, ignore_error: true) # :yield: path
|
|
block_given? or return enum_for(__method__, *paths, ignore_error: ignore_error)
|
|
|
|
fs_encoding = Encoding.find("filesystem")
|
|
|
|
paths.collect!{|d| raise Errno::ENOENT, d unless File.exist?(d); d.dup}.each do |path|
|
|
path = path.to_path if path.respond_to? :to_path
|
|
enc = path.encoding == Encoding::US_ASCII ? fs_encoding : path.encoding
|
|
ps = [path]
|
|
while file = ps.shift
|
|
catch(:prune) do
|
|
yield file.dup
|
|
begin
|
|
s = File.lstat(file)
|
|
rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG, Errno::EINVAL
|
|
raise unless ignore_error
|
|
next
|
|
end
|
|
if s.directory? then
|
|
begin
|
|
fs = Dir.children(file, encoding: enc)
|
|
rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG, Errno::EINVAL
|
|
raise unless ignore_error
|
|
next
|
|
end
|
|
fs.sort!
|
|
fs.reverse_each {|f|
|
|
f = File.join(file, f)
|
|
ps.unshift f
|
|
}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
nil
|
|
end
|
|
|
|
# :markup: markdown
|
|
#
|
|
# call-seq:
|
|
# Find.prune
|
|
#
|
|
# This method is meaningful only within a block given with Find.find.
|
|
#
|
|
# Inside such a block,
|
|
# "prunes" the traversed file tree by not descending into the current directory:
|
|
#
|
|
# ```ruby
|
|
# files = []
|
|
# Find.find('.') do |path|
|
|
# Find.prune if File.basename(path) == 'test'
|
|
# next unless File.file?(path) && File.extname(path) == '.rb'
|
|
# files << path
|
|
# end
|
|
# files.size # => 6690
|
|
# files.take(3) # => ["./KNOWNBUGS.rb", "./array.rb", "./ast.rb"]
|
|
# ```
|
|
#
|
|
def prune
|
|
throw :prune
|
|
end
|
|
|
|
module_function :find, :prune
|
|
end
|