Как создать каталог, если его нет, с помощью класса File в Ruby?

123

У меня есть такое заявление:

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

куда

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

Я хочу, чтобы произошло следующее: если в пути нет каталога с именем somedirили some_subdirили того и другого, я хочу, чтобы он автоматически создавал его.

Как я могу это сделать?

marcamillion
источник

Ответы:

155

Вы можете использовать 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виду , что оно по сути является повторной реализацией (это метод, предназначенный для вашего варианта использования)
Эврика,
57
Обратите внимание, что это FileUtils#mkdir_pработает, даже если иерархия каталогов уже существует (она просто ничего не делает), поэтому это решение можно сжать в однострочную банку с оператором require:FileUtils.mkdir_p(File.dirname(some_path))
Эврика,
1
@JosephK - для меня эта (вводящая в заблуждение) ошибка EEXIST оказалась проблемой с разрешением.
TomG
82

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

require 'fileutils'

FileUtils.mkdir_p 'dir_name'

На основании комментария Эврики .

Андрей Михайлов - лолмаус
источник
1
Это комментарий @Eureka - «Обратите внимание, что FileUtils # mkdir_p работает, даже если иерархия каталогов уже существует (она просто ничего не делает), поэтому это решение можно сжать в однострочную банку с оператором require FileUtils.mkdir_p(File.dirname(some_path))»
Дарпан
23
directory_name = "name"
Dir.mkdir(directory_name) unless File.exists?(directory_name)
Licysca
источник
2
Вы можете столкнуться с условиями гонки, используя этот метод, каталог может быть создан после File.exists? выполняется, но до выполнения Dir.mkdir.
Мэтт Фенелон
4

Судя по другим ответам, ничего не произошло (не сработало). Ошибки не было, и каталог не был создан.

Вот что мне нужно было сделать:

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

Мне нужно было создать переменную, чтобы поймать ответ, который FileUtils.mkdir_p('dir_name')отправляется обратно ... тогда все работало как шарм!

skplunkerin
источник
не имеет смысла. зачем нужно ловить отдачу?
Тим Кречмер
@huanson, мне не нужно было ловить отдачу ... но логика не работала, пока я не создал response = FileUtils.mkdir_p('dir_name'). Если бы я не создавал эту переменную, FileUtils.mkdir_p('dir_name')у меня не работало ... или, по крайней мере, то, что я помню, произошло (этому ответу больше года). Я не удивлюсь, если новая версия 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)
Ironsand
источник
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

И в нашем hooks.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

Мне было нужно это решение, потому что gem rmagick зависимостей FileUtils не позволил моему приложению Rails развернуться на Amazon Web Services, поскольку rmagick зависит от пакета libmagickwand-dev (Ubuntu) / imagemagick (OSX) для правильной работы.

Copyleft
источник