Як створити каталог, якщо в Ruby класу File не існує?


121

Я маю таке твердження:

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

Де

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

Те , що я хочу , щоб статися, якщо немає директорії somedirабо some_subdirабо як в дорозі, я хочу, щоб автоматично створити його.

Як я можу це зробити?

Відповіді:


154

Ви можете використовувати FileUtils для рекурсивного створення батьківських каталогів, якщо вони ще відсутні:

require 'fileutils'

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

Редагувати: Ось рішення з використанням лише основних бібліотек (повторне додавання колеса, не рекомендується)

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

4
FileUtils знаходиться у stdlib: ruby-doc.org/stdlib-1.9.3/libdoc/fileutils/rdoc/FileUtils.html
Еврика

О, добре. Я мав на увазі серцевину, а не stdlib. Так чи інакше, це добре. Це працює. Дякую!
marcamillion

1
Я додав основне рішення до своєї відповіді: Однак майте на увазі, що воно по суті повторюється FileUtils.mkdir_p(що є методом, присвяченим вашому випадку використання)
Eureka,

57
Зауважте, що це FileUtils#mkdir_pпрацює навіть у тому випадку, якщо ієрархія каталогів вже існує (вона просто нічого не робить), тому це рішення можна стиснути до цього може однокласника плюс необхідне твердження:FileUtils.mkdir_p(File.dirname(some_path))
Eureka

1
@JosephK - для мене ця (оманлива) помилка EEXIST виявилася проблемою дозволу.
TomG

81

Для тих, хто шукає спосіб створити каталог, якщо він не існує , ось просте рішення:

require 'fileutils'

FileUtils.mkdir_p 'dir_name'

На підставі коментаря Еврики .


1
Це коментар @ Eureka - "Зауважте, що FileUtils # mkdir_p працює, навіть якщо ієрархія каталогів вже існує (вона просто нічого не робить), тому це рішення може бути стиснене в цей може бути FileUtils.mkdir_p(File.dirname(some_path))
однолінійний

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

2
Ви можете зіткнутися з умовами гонки за допомогою цього методу, щоб каталог міг бути створений після File.exists? працює, але перед виконанням Dir.mkdir.
Метт Фенелон

4

На основі інших відповідей нічого не сталося (не вийшло). Не було помилок і не було створено каталогів.

Ось що мені потрібно було зробити:

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

Мені потрібно було створити змінну, щоб спіймати відповідь, що FileUtils.mkdir_p('dir_name')надсилає назад ... тоді все працювало як шарм!


не має сенсу. чому вам потрібно піймати прибуток?
Тім Кречмер

@huanson, мені не потрібно було вловлювати повернення ... але логіка не працювала, поки я не створив response = FileUtils.mkdir_p('dir_name'). Якщо я не створив цю змінну, FileUtils.mkdir_p('dir_name')не працював би для мене ... або, принаймні, це те, що я пригадую, сталося (ця відповідь більше 1 року). Я не був би здивований, якщо новіша версія Ruby виправить це питання.
skplunkerin

2

Як щодо використання Pathname?

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

1
Він працює some_path.dirname.mkpathзамістьsome_path.dirname.mkdir_p
Мауро Нідола

1
+1 у mkpath. Крім того, якщо ви просто маєте каталог, а не шлях, немає необхідності dirname, наприклад, Pathname ("somedir / some_subdir"). Mkpath буде працювати так само.
Майкл

1

У відповідності з подібними лініями (і в залежності від вашої структури) ми вирішили, де зберігати скріншоти:

У нашому налаштуваннях env (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

І в наших гачках.rb

  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

Рішення, що відповідає лише основній бібліотеці, було неповним. Якщо ви хочете використовувати лише основні бібліотеки, використовуйте наступне:

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

Мені потрібно було це рішення, тому що дорогоцінний камінь залежності FileUtils rmagick заважав моєму додатку Rails розгортатися на веб-сервісах Amazon, оскільки rmagick залежить від належної роботи пакета libmagickwand-dev (Ubuntu) / imagemagick (OSX).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.