Ruby 提供多種走訪目錄樹的方式。本文收集四種具代表性的寫法:手寫遞迴函式、Dir.entries 迭代、Python 對照版、以及最推薦的 Find.find / Dir.glob 優雅寫法,幫助你根據專案需求選擇最適合的方案。
def traverse_dir(file_path)
if File.directory?(file_path)
Dir.foreach(file_path) do |file|
next if file == '.' || file == '..'
traverse_dir(File.join(file_path, file))
end
else
puts "File: #{File.basename(file_path)}, Size: #{File.size(file_path)}"
end
end
traverse_dir('D:/apache-tomcat')
./..、路徑拼接、錯誤處理,程式碼冗長。
def get_file_list(path)
Dir.entries(path).each do |sub|
next if sub == '.' || sub == '..'
full = File.join(path, sub)
if File.directory?(full)
puts "[#{sub}]"
get_file_list(full)
else
puts " |--#{sub}"
end
end
end
import os
def get_file_list(dir, file_list):
if os.path.isfile(dir):
file_list.append(dir)
elif os.path.isdir(dir):
for s in os.listdir(dir):
get_file_list(os.path.join(dir, s), file_list)
return file_list
for f in get_file_list('D:/workspace', []):
print f
require 'find'
Find.find('/home/bruce') do |path|
puts path if File.file?(path)
end
./..、路徑拼接、權限錯誤。Find.prune 可跳過特定目錄遞迴。
# 所有檔案遞迴
Dir.glob("**/*", File::FNM_DOTMATCH).each { |f| puts f if File.file?(f) }
# 特定副檔名
Dir.glob("**/*.rb").each { |f| puts f }
# 遞迴迭代器(記憶體效率佳)
Dir.glob("**/*") do |f|
puts f if File.file?(f)
end
def iter_browse(path)
return enum_for(:iter_browse, path) unless block_given?
Find.find(path) do |f|
yield f if File.file?(f)
end
end
iter_browse("/home/bruce") { |full| puts full }
# 或
iter_browse("/home/bruce").each { |f| puts f }
| 方法 | 記憶體 | 速度 | 適用場景 |
|---|---|---|---|
| 手寫遞迴 | O(深度) | 慢 | 教學、特殊邏輯控制 |
| Dir.entries + 遞迴 | O(深度) | 中 | 簡單腳本 |
| Find.find | O(深度) | 快 | 標準遞迴走訪、需 prune |
| Dir.glob("**/*") | O(檔案數) | 快 | 模式匹配、一次性列表 |
| Dir.glob 區塊形式 | O(1) 流式 | 快 | 大量檔案、記憶體敏感 |
Find.find 不會自動偵測 symlink 循環,需自行維護 visited 集合。rescue Errno::EACCES。Encoding.default_external。日常腳本首選 Dir.glob("**/*.ext") 或 Find.find 區塊形式,簡潔、高效、記憶體友善。手寫遞迴僅用於教學或極特殊邏輯需求。理解 Find.prune、FNM_DOTMATCH、區塊與枚舉器的差異,能靈活應對各類目錄走訪任務。