Làm cách nào để tạo thư mục nếu không có thư mục nào tồn tại bằng cách sử dụng lớp Tệp trong Ruby?


121

Tôi có tuyên bố này:

File.open(some_path, 'w+') { |f| f.write(builder.to_html)  }

Ở đâu

some_path = "somedir/some_subdir/some-file.html"

Điều tôi muốn xảy ra là, nếu không có thư mục nào được gọi somedirhoặc some_subdirhoặc cả hai trong đường dẫn, tôi muốn nó tự động tạo nó.

Làm thế nào tôi có thể làm điều đó?

Câu trả lời:


154

Bạn có thể sử dụng FileUtils để tạo đệ quy các thư mục mẹ, nếu chúng chưa có:

require 'fileutils'

dirname = File.dirname(some_path)
unless File.directory?(dirname)
  FileUtils.mkdir_p(dirname)
end

Chỉnh sửa: Đây là một giải pháp chỉ sử dụng các thư viện cốt lõi (không nên thực hiện lại bánh xe)

dirname = File.dirname(some_path)
tokens = dirname.split(/[\/\\]/) # don't forget the backslash for Windows! And to escape both "\" and "/"

1.upto(tokens.size) do |n|
  dir = tokens[0...n]
  Dir.mkdir(dir) unless Dir.exist?(dir)
end


Ồ được thôi. Ý tôi là cốt lõi, không phải stdlib. Dù bằng cách nào, điều đó cũng tốt. Những công việc này. Cảm ơn!
marcamillion

1
Tôi đã thêm một giải pháp cốt lõi chỉ để câu trả lời của tôi: Hãy nhận biết, tuy nhiên, nó chủ yếu reimplements FileUtils.mkdir_p(đó là phương pháp dành riêng cho trường hợp sử dụng của bạn)
Eureka

57
Lưu ý rằng FileUtils#mkdir_phoạt động ngay cả hệ thống phân cấp thư mục đã tồn tại (nó chỉ không có gì) nên giải pháp này có thể được nén vào điều này có thể lót một cộng với một yêu cầu tuyên bố:FileUtils.mkdir_p(File.dirname(some_path))
Eureka

1
@JosephK - đối với tôi, lỗi EEXIST (gây hiểu lầm) này đã trở thành vấn đề về quyền.
TomG

81

Đối với những người đang tìm cách tạo một thư mục nếu nó không tồn tại , đây là giải pháp đơn giản:

require 'fileutils'

FileUtils.mkdir_p 'dir_name'

Dựa trên nhận xét của Eureka .


1
Đây là nhận xét của @ Eureka - "Lưu ý rằng FileUtils # mkdir_p hoạt động ngay cả khi hệ thống phân cấp thư mục đã tồn tại (nó chỉ không có tác dụng gì) vì vậy giải pháp này có thể được nén thành một lớp lót cùng với một câu lệnh yêu cầu: FileUtils.mkdir_p(File.dirname(some_path))"
Darpan

23
directory_name = "name"
Dir.mkdir(directory_name) unless File.exists?(directory_name)

2
Bạn có thể gặp phải các điều kiện chạy đua bằng cách sử dụng phương pháp này, thư mục có thể được tạo sau khi File.exists? chạy nhưng trước khi Dir.mkdir được thực thi.
Matt Fenelon

4

Dựa trên câu trả lời của người khác, không có gì xảy ra (không hoạt động). Không có lỗi và không có thư mục nào được tạo.

Đây là những gì tôi cần làm:

require 'fileutils'
response = FileUtils.mkdir_p('dir_name')

Tôi cần tạo một biến để bắt phản hồi FileUtils.mkdir_p('dir_name')gửi lại ... sau đó mọi thứ hoạt động như một sự quyến rũ!


không có ý nghĩa. tại sao bạn cần phải bắt được lợi nhuận?
Tim Kretschmer

@huanson, tôi không cần phải bắt trả lại ... nhưng logic không hoạt động cho đến khi tôi tạo response = FileUtils.mkdir_p('dir_name'). Nếu tôi không tạo biến này, FileUtils.mkdir_p('dir_name')không hoạt động với tôi ... hoặc ít nhất đó là điều tôi nhớ đã xảy ra (câu trả lời này đã hơn 1 năm). Tôi sẽ không ngạc nhiên nếu một phiên bản Ruby mới hơn khắc phục sự cố này.
skplunkerin

2

Làm thế nào về việc sử dụng Pathname?

require 'pathname'
some_path = Pathname("somedir/some_subdir/some-file.html")
some_path.dirname.mkdir_p
some_path.write(builder.to_html)

1
Nó hoạt động với some_path.dirname.mkpaththay vìsome_path.dirname.mkdir_p
Mauro Nidola

1
+1 trên mkpath. Ngoài ra, nếu bạn chỉ có thư mục chứ không phải đường dẫn thì không cần dirname, ví dụ: Pathname ("somedir / some_subdir"). Mkpath sẽ hoạt động theo cách tương tự.
Michael

1

Theo các dòng tương tự (và tùy thuộc vào cấu trúc của bạn), đây là cách chúng tôi giải quyết nơi lưu ảnh chụp màn hình:

Trong thiết lập env của chúng tôi (env.rb)

screenshotfolder = "./screenshots/#{Time.new.strftime("%Y%m%d%H%M%S")}"
unless File.directory?(screenshotfolder)
  FileUtils.mkdir_p(screenshotfolder)
end
Before do
  @screenshotfolder = screenshotfolder
  ...
end

Và trong hooks.rb của chúng tôi

  screenshotName = "#{@screenshotfolder}/failed-#{scenario_object.title.gsub(/\s+/,"_")}-#{Time.new.strftime("%Y%m%d%H%M%S")}_screenshot.png";
  @browser.take_screenshot(screenshotName) if scenario.failed?

  embed(screenshotName, "image/png", "SCREENSHOT") if scenario.failed?

1

Giải pháp duy nhất cho "thư viện cốt lõi" của câu trả lời hàng đầu là không đầy đủ. Nếu bạn chỉ muốn sử dụng các thư viện lõi, hãy sử dụng như sau:

target_dir = ""

Dir.glob("/#{File.join("**", "path/to/parent_of_some_dir")}") do |folder|
  target_dir = "#{File.expand_path(folder)}/somedir/some_subdir/"
end

# Splits name into pieces
tokens = target_dir.split(/\//)

# Start at '/'
new_dir = '/'

# Iterate over array of directory names
1.upto(tokens.size - 1) do |n|

  # Builds directory path one folder at a time from top to bottom
  unless n == (tokens.size - 1)
    new_dir << "#{tokens[n].to_s}/" # All folders except innermost folder
  else
    new_dir << "#{tokens[n].to_s}" # Innermost folder
  end

  # Creates directory as long as it doesn't already exist
  Dir.mkdir(new_dir) unless Dir.exist?(new_dir)
end

Tôi cần giải pháp này vì gem rmagick phụ thuộc của FileUtils đã ngăn ứng dụng Rails của tôi triển khai trên Amazon Web Services vì ​​rmagick phụ thuộc vào gói libmagickwand-dev (Ubuntu) / imagemagick (OSX) để hoạt động bình thường.

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.