Nhận tên của tất cả các tệp từ một thư mục với Ruby


Câu trả lời:


537

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"]

5
Hoặc bạn có thể làm tương tự với Dir :: global ()
Yoann Le Touche

2
Ngoài ra, sử dụng ./...chứ không phải~/
Minh Triết

5
Tại sao điều này được ưa thích?
BvuRVKyUVlViVIc7

1
@MinhTriet làm gì vậy? Nó là gì thích hợp hơn?
stephenmurdoch

9
@marflar - ./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.
mirichan

170
Dir.entries(folder)

thí dụ:

Dir.entries(".")

Nguồn: http://ruby-doc.org/core/groupes/Dir.html#method-c-entries


15
Có vẻ như anh ấy đang sử dụng SO để ghi lại câu trả lời cho câu hỏi anh ấy vừa hỏi. Một loại ghi nhớ, tôi cho rằng. Không thể thấy sai nhiều về điều đó - sau tất cả, mặc dù điều này hơi không đầy đủ ( Dir#globchẳ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" ...
Mike Woodhouse

1
@Mike: Trong sơ đồ lớn của mọi thứ, nó có lẽ không phải là một vấn đề lớn. Và như bạn nói nếu các câu hỏi và câu trả lời là tốt, nó có thể là một điểm cộng tổng thể cho trang web. Nhưng ở đây cả câu hỏi và câu trả lời đều tối thiểu đến mức nó dường như không đặc biệt hữu ích.
Telemachus

17
@Telemachus Tôi Dirhiế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.
Željko Filipin

Đây có thể là một lựa chọn tốt hơn Dir[]hoặc Dir.globkhi đố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.
Benjamin Oakes

92

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}

19
Cũng có thể làm ...select {|f| File.file? f}cho ý nghĩa rõ ràng hơn và cú pháp ngắn hơn.
Automatico

2
@squixy Bạn có viết nó ra một cách chính xác không?:Dir.entries("your/folder").select {|f| File.file? f}
Automatico

9
Vâng. !File.directory?đang làm việc nhưng File.file?không.
Kamil Lelonek

2
@squixy Tôi gặp vấn đề tương tự, trong trường hợp của tôi, tôi cần cung cấp đường dẫn đầy đủ, không chỉ tên tệp được Dir.foreach trả về
TheLukeMcCarthy

6
.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.
Ian

36

Để 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 }

Giải pháp thay thế

Sử dụng Find#findphương pháp tra cứu dựa trên mẫu như Dir.globthự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?" .


18

Đ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] .dotfiletrên unix, tôi không biết về Windows



9

Cá nhân, tôi thấy điều này hữu ích nhất để lặp qua các tệp trong một thư mục, hướng tới sự an toàn:

Dir['/etc/path/*'].each do |file_name|
  next if File.directory? file_name 
end

9

Đâ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

6

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.entriestrả 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.
Nathan

Thật kỳ lạ khi mã không hoạt động trong trường hợp của bạn. Vì đây là mã tôi đã sử dụng trong một ứng dụng trực tiếp hoạt động tốt. Tôi sẽ kiểm tra lại mã của mình và đăng ở đây nếu thiếu mã làm việc ban đầu :)
Lahiru

1
@Nathan Xem câu trả lời của tôi để được giải thích


4

Đây là những gì làm việc cho tôi:

Dir.entries(dir).select { |f| File.file?(File.join(dir, f)) }

Dir.entriestrả 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 dirbằ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().


1
Bạn cần loại trừ "." và ".." từ các mục
Edgar Ortega

3

Bạn cũng có thể muốn sử dụng Rake::FileList(miễn là bạn có rakesự 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.

https://docs.ruby-lang.org/en/2.1.0/Rake/FileList.html


1

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.


Hoặc chỉ sử dụng*.*
Richard Peck

1
Dir.new('/home/user/foldername').each { |file| puts file }

1

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.entriesbao gồm các tập tin ẩn theo mặc định. Dir.entriescuố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.


1

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

0
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


0

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")

Giải pháp này hiệu quả với tôi vì tôi có một giải pháp kế thừa với phiên bản ruby ​​cũ không hỗ trợ lệnh Dir.children
Ciprian Dragoe 16/07/19
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.