Bagaimana cara membuat direktori jika tidak ada menggunakan kelas File di Ruby?

121

Saya memiliki pernyataan ini:

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

Dimana

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

Apa yang saya inginkan terjadi adalah, jika tidak ada direktori yang dipanggil somediratau some_subdiratau keduanya di jalur, saya ingin membuatnya secara otomatis.

Bagaimana saya bisa melakukan itu?

marcamillion
sumber

Jawaban:

154

Anda dapat menggunakan FileUtils untuk membuat direktori induk secara rekursif, jika belum ada:

require 'fileutils'

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

Sunting: Berikut adalah solusi hanya menggunakan pustaka inti (menerapkan ulang roda, tidak disarankan)

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
Eureka
sumber
Oh oke. Maksudku intinya, bukan stdlib. Bagaimanapun, itu bagus. Ini bekerja. Terima kasih!
marcamillion
1
Saya menambahkan solusi hanya inti untuk jawaban saya: Namun, ketahuilah, bahwa itu pada dasarnya reimplements FileUtils.mkdir_p(yang merupakan metode yang didedikasikan untuk kasus penggunaan Anda)
Eureka
57
Perhatikan bahwa itu FileUtils#mkdir_pberfungsi bahkan jika hierarki direktori sudah ada (itu tidak melakukan apa-apa) sehingga solusi ini dapat dikompresi menjadi satu baris ini ditambah pernyataan yang diperlukan:FileUtils.mkdir_p(File.dirname(some_path))
Eureka
1
@JosephK - bagi saya kesalahan EEXIST (menyesatkan) ini akhirnya menjadi masalah izin.
TomG
81

Bagi mereka yang mencari cara untuk membuat direktori jika tidak ada , inilah solusi sederhananya:

require 'fileutils'

FileUtils.mkdir_p 'dir_name'

Berdasarkan komentar Eureka .

Andrey Mikhaylov - lolmaus
sumber
1
Ini adalah komentar @ Eureka - "Perhatikan bahwa FileUtils # mkdir_p berfungsi meskipun hierarki direktori sudah ada (tidak melakukan apa-apa) sehingga solusi ini dapat dikompresi menjadi satu baris ini ditambah pernyataan yang diperlukan: FileUtils.mkdir_p(File.dirname(some_path))"
Darpan
23
directory_name = "name"
Dir.mkdir(directory_name) unless File.exists?(directory_name)
Licysca
sumber
2
Anda mungkin mengalami kondisi balapan menggunakan metode ini, direktori dapat dibuat setelah File.exists? berjalan tetapi sebelum Dir.mkdir dijalankan.
Matt Fenelon
4

Berdasarkan jawaban orang lain, tidak ada yang terjadi (tidak berhasil). Tidak ada kesalahan, dan tidak ada direktori yang dibuat.

Inilah yang perlu saya lakukan:

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

Saya perlu membuat variabel untuk menangkap respons yang FileUtils.mkdir_p('dir_name')dikirim kembali ... lalu semuanya bekerja seperti pesona!

skplunkerin
sumber
tidak masuk akal. mengapa Anda perlu mengejar keuntungan?
Tim Kretschmer
@huanson, saya tidak perlu menangkap pengembalian ... tetapi logikanya tidak berfungsi sampai saya membuatnya response = FileUtils.mkdir_p('dir_name'). Jika saya tidak membuat variabel ini, FileUtils.mkdir_p('dir_name')tidak berfungsi untuk saya ... atau setidaknya itulah yang saya ingat terjadi (jawaban ini berumur lebih dari 1 tahun). Saya tidak akan terkejut jika versi Ruby yang lebih baru memperbaiki masalah ini.
skplunkerin
2

Bagaimana kalau menggunakan Pathname?

require 'pathname'
some_path = Pathname("somedir/some_subdir/some-file.html")
some_path.dirname.mkdir_p
some_path.write(builder.to_html)
pasir besi
sumber
1
Ini berfungsi dengan some_path.dirname.mkpathalih - alihsome_path.dirname.mkdir_p
Mauro Nidola
1
+1 aktif mkpath. Juga jika Anda hanya memiliki direktori dan bukan path, tidak perlu dirname, misalnya Pathname ("somedir / some_subdir"). Mkpath akan bekerja dengan cara yang sama.
Michael
1

Sepanjang garis yang sama (dan tergantung pada struktur Anda), berikut adalah cara kami menyelesaikan tempat menyimpan tangkapan layar:

Dalam pengaturan env kami (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

Dan di hooks.rb kami

  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?
Shell Bryson
sumber
1

Solusi satu-satunya "pustaka inti" dari jawaban teratas tidak lengkap. Jika Anda hanya ingin menggunakan pustaka inti, gunakan yang berikut ini:

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

Saya membutuhkan solusi ini karena permata ketergantungan FileUtils 'rmagick mencegah aplikasi Rails saya diterapkan di Amazon Web Services karena rmagick bergantung pada paket libmagickwand-dev (Ubuntu) / imagemagick (OSX) untuk bekerja dengan benar.

CopyLeft
sumber