Find 모듈

Find 모듈 (Find)

파일 경로 집합을 위에서 아래로(top-down) 순회하는 기능을 제공하는 모듈이에요. 디렉토리 트리를 재귀적으로 돌며 각 파일·디렉토리마다 블록을 실행해요.

예를 들어 홈 디렉토리 아래 모든 파일 크기의 합을 구하되, "점(dot)"으로 시작하는 디렉토리(예: $HOME/.ssh)는 무시한다고 해볼게요.

require 'find'

total_size = 0

Find.find(ENV["HOME"]) do |path|
  if FileTest.directory?(path)
    if File.basename(path).start_with?('.')
      Find.prune       # Don't look any further into this directory.
    else
      next
    end
  else
    total_size += FileTest.size(path)
  end
end

출처: Ruby 3.3 API

본문

Find.find는 인자로 준 경로에서 시작해 하위 디렉토리까지 재귀적으로 탐색해요. 탐색을 끊고 싶은 디렉토리가 있으면 Find.prune으로 그 지점부터 건너뛸 수 있어요.

상수 (Constants)

  • VERSION — 모듈 버전

::find (Public Class Method)

find(*paths, ignore_error: true) { |path| ... } — 인자로 나열된 모든 파일·디렉토리 이름으로 블록을 호출하고, 그 하위 디렉토리로 재귀하며 같은 작업을 반복해요. 블록이 없으면 인자 대신 열거자(enumerator)를 돌려줘요. ignore_error: true일 때 접근 불가·없는 경로 같은 오류는 무시하고 계속 진행해요.

# File lib/find.rb, line 40
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

::prune (Public Class Method)

prune() — 현재 파일이나 디렉토리를 건너뛰고, 루프를 다음 항목부터 다시 시작해요. 현재 항목이 디렉토리면 그 디렉토리로는 재귀 진입하지 않아요. Find::find와 연결된 블록 안에서만 의미가 있어요.

# File lib/find.rb, line 85
def prune
  throw :prune
end

위 예시에서 .ssh 같은 숨김 디렉토리를 만나면 Find.prune을 호출해 그 안으로 내려가지 않게 해요. 덕분에 불필요한 탐색을 끊을 수 있어요.