Lặp lại qua mỗi tệp trong một thư mục


274

Làm cách nào để viết một vòng lặp trong ruby ​​để tôi có thể thực thi một khối mã trên mỗi tệp?

Tôi chưa quen với ruby ​​và tôi đã kết luận rằng cách để làm điều này là thực hiện từng vòng lặp.
Tệp ruby ​​sẽ được thực thi từ một thư mục khác với thư mục tôi muốn lặp qua.

Tôi đã thử Dir.foreachvà tôi không thể làm cho nó hoạt động.


2
Bạn có thể chỉ định những gì đã xảy ra khi bạn cố gắng làm cho nó hoạt động? Mã chính xác nào bạn đã thử (hoặc đoạn có liên quan, nếu nó dài)? Những thông báo lỗi bạn đã nhận được? Dir.foreachhoạt động để lặp lại nội dung của một thư mục, vì vậy một cái gì đó khác đang diễn ra.
Telemachus

3
Nếu bạn chỉ muốn các tệp trong thư mục của mình, đừng quên kiểm tra các tệp khi lặp qua nội dung thư mục: do_something_with(entry) if File.file?(entry)
glenn jackman

3
Sử dụng 'img/*.{jpg,png,gif,jpeg}'để lấy nhiều phần mở rộng.
Benjamin Crouzier

@ChrisPeter dường như không may, vì OP đã không ở trên trang này trong hơn bốn năm.
Joe Kennedy

Câu trả lời:


429

Như những người khác đã nói, Dir::foreachlà một lựa chọn tốt ở đây. Tuy nhiên, lưu ý rằng Dir::foreachDir::entriessẽ luôn bao gồm ...(thư mục hiện tại và thư mục gốc). Nói chung, bạn sẽ không muốn làm việc với chúng, vì vậy bạn có thể sử dụng Dir::each_childhoặc Dir::children(như được đề xuất bởi ma11hew28 ) hoặc làm một cái gì đó như thế này:

Dir.foreach('/path/to/dir') do |filename|
  next if filename == '.' or filename == '..'
  # Do work on the remaining files & directories
end

Dir::foreachDir::entries(cũng như Dir::each_childDir::children) cũng bao gồm các tập tin và thư mục ẩn. Thường thì đây là những gì bạn muốn, nhưng nếu không, bạn cần phải làm gì đó để bỏ qua chúng.

Ngoài ra, bạn có thể muốn xem xét trong Dir::globđó cung cấp kết hợp ký tự đại diện đơn giản:

Dir.glob('/path/to/dir/*.rb') do |rb_filename|
  # Do work on files & directories ending in .rb
end

12
Sử dụng Dir.foreachnếu thư mục chứa một số lượng lớn các tập tin!
Tilo

5
cảm ơn! Mod nhỏ để làm cho nó thậm chí còn tốt hơn:next if File.directory? item
mr.buttons

@ mr.buttons Điều đó sẽ không luôn luôn làm điều đúng đắn. Đôi khi mọi người muốn làm việc trên các thư mục cũng như các tập tin. Tôi đã đưa ra mã để tránh các danh sách đặc biệt cho .hoặc ..vì mọi người hầu như luôn muốn bỏ qua hai danh sách đó.
Telemachus

3
@Tilo: chỉ cần quan tâm, quan tâm để giải thích chi tiết hơn một chút tại sao? :)
mkataja

11
@mkataja Dir.foreachlặp đi lặp lại thay vì xây dựng một mảng (có khả năng rất lớn) lên phía trước (mà Dir.globkhông). Vì vậy, nếu thư mục thực sự lớn, nó có thể tạo ra sự khác biệt về hiệu suất. Trong trường hợp bình thường, bạn sẽ không nhận thấy, nhưng trong điều kiện căng thẳng, nó hoàn toàn có thể quan trọng.
Telemachus

99

Đây là phương pháp yêu thích của tôi để dễ đọc:

Dir.glob("*/*.txt") do |my_text_file|
  puts "working on: #{my_text_file}..."
end

Và bạn thậm chí có thể mở rộng điều này để hoạt động trên tất cả các tệp trong thư mục con:

Dir.glob("**/*.txt") do |my_text_file| # note one extra "*"
  puts "working on: #{my_text_file}..."
end

30

Dir cũng có cú pháp ngắn hơn để lấy một mảng của tất cả các tệp từ thư mục:

Dir['dir/to/files/*'].each do |fname|
    # do something with fname
end

Điều gì trong mã này ngăn các thư mục cũng được sử dụng với fnamecác lần lặp lại?
kayleeFrye_onDeck

26
Dir.foreach("/home/mydir") do |fname|
  puts fname
end

2
Hoặc sử dụng Dir # [] hoặc Dir # global
Ryan Bigg

13

Thư viện find được thiết kế cho nhiệm vụ này một cách cụ thể: https://ruby-doc.org/stdlib-2.5.1/libdoc/find/rdoc/Find.html

require 'find'
Find.find(path) do |file|
  # process
end

Đây là một thư viện ruby ​​tiêu chuẩn, vì vậy nó nên có sẵn


1
File.findđi theo cách đệ quy xuống hết mức có thể, bắt đầu từ bất kỳ con đường nào bạn đưa ra. Tôi không chắc đó là điều OP muốn.
Telemachus

Tôi dường như không có quyền truy cập vào phương thức đó - Find.find? Tôi có cần tải xuống một thư viện ot bao gồm chức năng này không?
bầu trời xanh

@ user470184: "Tìm" là một thư viện ruby ​​tiêu chuẩn và nên có sẵn với cài đặt ruby ​​mặc định. Tuy nhiên, bạn cần "yêu cầu 'tìm'" trước khi có thể sử dụng nó.
Faisal

1
@Faisal Cần tôi vượt qua mẫu glob như *.rbđểfind()
Ashhar Hasan

7

Tôi thích cái này, cái đó chưa được đề cập ở trên.

require 'pathname'

Pathname.new('/my/dir').children.each do |path|
    puts path
end

Lợi ích là bạn có được một đối tượng Pathname thay vì một chuỗi, rằng bạn có thể thực hiện các công cụ hữu ích và duyệt qua.


3
Dir.new('/my/dir').each do |name|
  ...
end

1
Ngoài Dir.new ('/ my / dir') còn có Dir.entries ('/ my / dir') nhưng Dir.foreach () ngắn gọn hơn một chút.
Tin Man

5
@ZED Cũng Dir.foreachlặp đi lặp lại trong khi Dir.entriesxây dựng toàn bộ mảng cùng một lúc. Vì vậy, nếu thư mục là rất lớn, nó sẽ ít bị ảnh hưởng bởi bộ nhớ. (Thường không phải là một vấn đề lớn, có lẽ, nhưng vẫn ...)
Telemachus

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.