Tôi muốn nhận tất cả tên tệp từ một thư mục bằng Ruby.
Tôi muốn nhận tất cả tên tệp từ một thư mục bằng Ruby.
Câu trả lời:
Bạn cũng có tùy chọn phím tắt là
Dir["/path/to/search/*"]
và nếu bạn muốn tìm tất cả các tệp Ruby trong bất kỳ thư mục hoặc thư mục con nào:
Dir["/path/to/search/**/*.rb"]
./...
chứ không phải~/
./
có nghĩa là thư mục hiện tại, trong khi đó /
là điểm gắn kết gốc và ~/
là thư mục chính của người dùng. Nếu bạn chuyển toàn bộ dự án sang một nơi khác, dự án đầu tiên sẽ hoạt động, nhưng hai dự án còn lại có thể sẽ không.
Dir.entries(folder)
thí dụ:
Dir.entries(".")
Nguồn: http://ruby-doc.org/core/groupes/Dir.html#method-c-entries
Dir#glob
chẳng hạn có lẽ đã được đề cập) chẳng có gì ngăn cản người khác đăng Câu trả lời thực sự tốt. Tất nhiên, tôi chủ yếu là một chàng trai "nửa ly" ...
Dir
hiếm khi sử dụng , và mỗi khi tôi cần, tôi phải đọc tài liệu. Tôi đã đăng câu hỏi và câu trả lời của mình lên đây để tôi có thể tìm thấy nó sau này, và thậm chí có thể giúp ai đó có cùng câu hỏi. Tôi nghĩ rằng tôi đã nghe ở podcast SO rằng không có gì sai với hành vi như vậy. Nếu bạn có một câu trả lời tốt hơn, xin vui lòng gửi nó. Tôi đã đăng những gì tôi biết, tôi không phải là ninja ninja. Tôi thường xuyên chấp nhận câu trả lời với nhiều phiếu nhất.
Dir[]
hoặc Dir.glob
khi đối số là một biến. Khi path = '/tmp'
, so sánh: Dir.glob("#{path}/*")
vs Dir.entries(path)
. Các giá trị trả về hơi khác nhau (".", ".."), nhưng giá trị sau dễ dàng hơn trong một cái nhìn nhanh.
Các đoạn mã sau đây hiển thị chính xác tên của các tệp trong một thư mục, bỏ qua các thư mục con và "."
, ".."
các thư mục chấm:
Dir.entries("your/folder").select {|f| !File.directory? f}
...select {|f| File.file? f}
cho ý nghĩa rõ ràng hơn và cú pháp ngắn hơn.
Dir.entries("your/folder").select {|f| File.file? f}
!File.directory?
đang làm việc nhưng File.file?
không.
.reject {|f| File.directory? f}
Có vẻ sạch sẽ hơn .select{|f| !File.directory? f}
. Ồ, và bây giờ tôi thấy bình luận đầu tiên ... cũng tốt.
Để có được tất cả các tệp (chỉ nghiêm ngặt các tệp) theo cách đệ quy:
Dir.glob('path/**/*').select{ |e| File.file? e }
Hoặc bất cứ thứ gì không phải là thư mục ( File.file?
sẽ từ chối các tệp không thông thường):
Dir.glob('path/**/*').reject{ |e| File.directory? e }
Sử dụng Find#find
phương pháp tra cứu dựa trên mẫu như Dir.glob
thực sự tốt hơn. Xem câu trả lời này cho "Thư mục một danh sách đệ quy trong Ruby?" .
Điều này làm việc cho tôi:
Nếu bạn không muốn các tập tin ẩn [1], hãy sử dụng Dir [] :
# With a relative path, Dir[] will return relative paths
# as `[ './myfile', ... ]`
#
Dir[ './*' ].select{ |f| File.file? f }
# Want just the filename?
# as: [ 'myfile', ... ]
#
Dir[ '../*' ].select{ |f| File.file? f }.map{ |f| File.basename f }
# Turn them into absolute paths?
# [ '/path/to/myfile', ... ]
#
Dir[ '../*' ].select{ |f| File.file? f }.map{ |f| File.absolute_path f }
# With an absolute path, Dir[] will return absolute paths:
# as: [ '/home/../home/test/myfile', ... ]
#
Dir[ '/home/../home/test/*' ].select{ |f| File.file? f }
# Need the paths to be canonical?
# as: [ '/home/test/myfile', ... ]
#
Dir[ '/home/../home/test/*' ].select{ |f| File.file? f }.map{ |f| File.expand_path f }
Bây giờ, Dir.entries sẽ trả về các tệp bị ẩn và bạn không cần dấu hoa thị ký tự đại diện (bạn chỉ có thể truyền biến với tên thư mục), nhưng nó sẽ trả về tên cơ sở trực tiếp, vì vậy các hàm File.xxx sẽ không hoạt động .
# In the current working dir:
#
Dir.entries( '.' ).select{ |f| File.file? f }
# In another directory, relative or otherwise, you need to transform the path
# so it is either absolute, or relative to the current working dir to call File.xxx functions:
#
home = "/home/test"
Dir.entries( home ).select{ |f| File.file? File.join( home, f ) }
[1] .dotfile
trên unix, tôi không biết về Windows
Trong Ruby 2.5 bây giờ bạn có thể sử dụng Dir.children
. Nó được đặt tên tập tin dưới dạng một mảng ngoại trừ "." và ".."
Thí dụ:
Dir.children("testdir") #=> ["config.h", "main.rb"]
Đây là một giải pháp để tìm các tập tin trong một thư mục:
files = Dir["/work/myfolder/**/*.txt"]
files.each do |file_name|
if !File.directory? file_name
puts file_name
File.open(file_name) do |file|
file.each_line do |line|
if line =~ /banco1/
puts "Found: #{line}"
end
end
end
end
end
Trong khi nhận tất cả các tên tệp trong một thư mục, đoạn mã này có thể được sử dụng để từ chối cả hai thư mục [ .
, ..
] và các tệp ẩn bắt đầu bằng một.
files = Dir.entries("your/folder").reject {|f| File.directory?(f) || f[0].include?('.')}
Dir.entries
trả về tên tệp cục bộ, không phải đường dẫn tệp tuyệt đối. Mặt khác, File.directory?
mong đợi một đường dẫn tệp tuyệt đối. Mã này không hoạt động như mong đợi.
mã này chỉ trả về tên tệp có phần mở rộng của chúng (không có đường dẫn chung)
Dir.children("/path/to/search/")
Đây là những gì làm việc cho tôi:
Dir.entries(dir).select { |f| File.file?(File.join(dir, f)) }
Dir.entries
trả về một chuỗi các chuỗi. Sau đó, chúng tôi phải cung cấp một đường dẫn đầy đủ của tệp tới File.file?
, trừ khi dir
bằng với thư mục làm việc hiện tại của chúng tôi. Đó là lý do tại sao điều này File.join()
.
Bạn cũng có thể muốn sử dụng Rake::FileList
(miễn là bạn có rake
sự phụ thuộc):
FileList.new('lib/*') do |file|
p file
end
Theo API:
FileLists lười biếng. Khi được cung cấp một danh sách các mẫu hình cầu cho các tệp có thể được đưa vào danh sách tệp, thay vì tìm kiếm cấu trúc tệp để tìm tệp, FileList giữ mẫu để sử dụng sau.
Nếu bạn muốn có được một loạt tên tệp bao gồm cả liên kết tượng trưng , hãy sử dụng
Dir.new('/path/to/dir').entries.reject { |f| File.directory? f }
hoặc thậm chí
Dir.new('/path/to/dir').reject { |f| File.directory? f }
và nếu bạn muốn đi mà không có symlink , hãy sử dụng
Dir.new('/path/to/dir').select { |f| File.file? f }
Như thể hiện trong các câu trả lời khác, hãy sử dụng Dir.glob('/path/to/dir/**/*')
thay vì Dir.new('/path/to/dir')
nếu bạn muốn nhận tất cả các tệp theo cách đệ quy.
*.*
Ngoài các đề xuất trong chủ đề này, tôi muốn đề cập rằng nếu bạn cũng cần trả lại các tệp dấu chấm (.gitignore, v.v.), với Dir.glob, bạn sẽ cần bao gồm một cờ như vậy:
Dir.glob("/path/to/dir/*", File::FNM_DOTMATCH)
Theo mặc định, Dir.entries bao gồm các tập tin dấu chấm, cũng như các thư mục mẹ hiện tại.
Đối với bất kỳ ai quan tâm, tôi tò mò làm thế nào các câu trả lời ở đây so với nhau trong thời gian thực hiện, đây là kết quả chống lại hệ thống phân cấp lồng nhau sâu sắc. Ba kết quả đầu tiên là không đệ quy:
user system total real
Dir[*]: (34900 files stepped over 100 iterations)
0.110729 0.139060 0.249789 ( 0.249961)
Dir.glob(*): (34900 files stepped over 100 iterations)
0.112104 0.142498 0.254602 ( 0.254902)
Dir.entries(): (35600 files stepped over 100 iterations)
0.142441 0.149306 0.291747 ( 0.291998)
Dir[**/*]: (2211600 files stepped over 100 iterations)
9.399860 15.802976 25.202836 ( 25.250166)
Dir.glob(**/*): (2211600 files stepped over 100 iterations)
9.335318 15.657782 24.993100 ( 25.006243)
Dir.entries() recursive walk: (2705500 files stepped over 100 iterations)
14.653018 18.602017 33.255035 ( 33.268056)
Dir.glob(**/*, File::FNM_DOTMATCH): (2705500 files stepped over 100 iterations)
12.178823 19.577409 31.756232 ( 31.767093)
Chúng được tạo với kịch bản điểm chuẩn sau:
require 'benchmark'
base_dir = "/path/to/dir/"
n = 100
Benchmark.bm do |x|
x.report("Dir[*]:") do
i = 0
n.times do
i = i + Dir["#{base_dir}*"].select {|f| !File.directory? f}.length
end
puts " (#{i} files stepped over #{n} iterations)"
end
x.report("Dir.glob(*):") do
i = 0
n.times do
i = i + Dir.glob("#{base_dir}/*").select {|f| !File.directory? f}.length
end
puts " (#{i} files stepped over #{n} iterations)"
end
x.report("Dir.entries():") do
i = 0
n.times do
i = i + Dir.entries(base_dir).select {|f| !File.directory? File.join(base_dir, f)}.length
end
puts " (#{i} files stepped over #{n} iterations)"
end
x.report("Dir[**/*]:") do
i = 0
n.times do
i = i + Dir["#{base_dir}**/*"].select {|f| !File.directory? f}.length
end
puts " (#{i} files stepped over #{n} iterations)"
end
x.report("Dir.glob(**/*):") do
i = 0
n.times do
i = i + Dir.glob("#{base_dir}**/*").select {|f| !File.directory? f}.length
end
puts " (#{i} files stepped over #{n} iterations)"
end
x.report("Dir.entries() recursive walk:") do
i = 0
n.times do
def walk_dir(dir, result)
Dir.entries(dir).each do |file|
next if file == ".." || file == "."
path = File.join(dir, file)
if Dir.exist?(path)
walk_dir(path, result)
else
result << file
end
end
end
result = Array.new
walk_dir(base_dir, result)
i = i + result.length
end
puts " (#{i} files stepped over #{n} iterations)"
end
x.report("Dir.glob(**/*, File::FNM_DOTMATCH):") do
i = 0
n.times do
i = i + Dir.glob("#{base_dir}**/*", File::FNM_DOTMATCH).select {|f| !File.directory? f}.length
end
puts " (#{i} files stepped over #{n} iterations)"
end
end
Sự khác biệt về số lượng tập tin là do Dir.entries
bao gồm các tập tin ẩn theo mặc định. Dir.entries
cuối cùng mất nhiều thời gian hơn trong trường hợp này do cần phải xây dựng lại đường dẫn tuyệt đối của tệp để xác định xem một tệp có phải là thư mục hay không, nhưng ngay cả khi không có nó vẫn mất nhiều thời gian hơn các tùy chọn khác trong trường hợp đệ quy. Đây là tất cả sử dụng ruby 2.5.1 trên OSX.
Một cách đơn giản có thể là:
dir = './' # desired directory
files = Dir.glob(File.join(dir, '**', '*')).select{|file| File.file?(file)}
files.each do |f|
puts f
end
def get_path_content(dir)
queue = Queue.new
result = []
queue << dir
until queue.empty?
current = queue.pop
Dir.entries(current).each { |file|
full_name = File.join(current, file)
if not (File.directory? full_name)
result << full_name
elsif file != '.' and file != '..'
queue << full_name
end
}
end
result
end
trả về các đường dẫn tương đối của tệp từ thư mục và tất cả các thư mục con
Trong ngữ cảnh IRB, bạn có thể sử dụng cách sau để lấy các tệp trong thư mục hiện tại:
file_names = `ls`.split("\n")
Bạn cũng có thể làm việc này trên các thư mục khác:
file_names = `ls ~/Documents`.split("\n")