Wie erstelle ich ein Verzeichnis, wenn mit der Dateiklasse in Ruby kein Verzeichnis vorhanden ist?

121

Ich habe diese Aussage:

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

Wo

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

Was ich tun möchte, ist, wenn es kein Verzeichnis mit dem Namen somediroder some_subdiroder beides im Pfad gibt, möchte ich, dass es automatisch erstellt wird.

Wie kann ich das machen?

Marcamillion
quelle

Antworten:

154

Mit FileUtils können Sie rekursiv übergeordnete Verzeichnisse erstellen, sofern diese noch nicht vorhanden sind:

require 'fileutils'

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

Bearbeiten: Hier ist eine Lösung, die nur die Kernbibliotheken verwendet (Neuimplementierung des Rads, nicht empfohlen)

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
quelle
4
FileUtils befindet sich in der stdlib: ruby-doc.org/stdlib-1.9.3/libdoc/fileutils/rdoc/FileUtils.html
Eureka
Oh ok. Ich meinte den Kern, nicht die stdlib. So oder so, das ist in Ordnung. Das funktioniert. Vielen Dank!
Marcamillion
1
Ich habe meiner Antwort eine Nur-Kern-Lösung hinzugefügt: Beachten Sie jedoch, dass sie im Wesentlichen neu implementiert wird FileUtils.mkdir_p(dies ist die Methode, die Ihrem Anwendungsfall gewidmet ist)
Eureka,
57
Beachten Sie, dass dies FileUtils#mkdir_pauch dann funktioniert, wenn die Verzeichnishierarchie bereits vorhanden ist (es wird einfach nichts unternommen), sodass diese Lösung in diesen Einzeiler mit einer erforderlichen Anweisung komprimiert werden kann:FileUtils.mkdir_p(File.dirname(some_path))
Eureka
1
@JosephK - für mich war dieser (irreführende) EEXIST-Fehler ein Berechtigungsproblem.
TomG
81

Für diejenigen, die nach einer Möglichkeit suchen , ein Verzeichnis zu erstellen, wenn es nicht vorhanden ist , ist hier die einfache Lösung:

require 'fileutils'

FileUtils.mkdir_p 'dir_name'

Basierend auf Eurekas Kommentar .

Andrey Mikhaylov - lolmaus
quelle
1
Dies ist @ Eurekas Kommentar - "Beachten Sie, dass FileUtils # mkdir_p auch dann funktioniert, wenn die Verzeichnishierarchie bereits vorhanden ist (es tut einfach nichts), sodass diese Lösung in diesen Einzeiler mit einer erforderlichen Anweisung komprimiert werden kann: FileUtils.mkdir_p(File.dirname(some_path))"
Darpan
23
directory_name = "name"
Dir.mkdir(directory_name) unless File.exists?(directory_name)
Licysca
quelle
2
Mit dieser Methode können Sie auf Rennbedingungen stoßen. Das Verzeichnis könnte erstellt werden, nachdem File.exists? läuft aber bevor Dir.mkdir ausgeführt wird.
Matt Fenelon
4

Basierend auf anderen Antworten ist nichts passiert (hat nicht funktioniert). Es gab keinen Fehler und es wurde kein Verzeichnis erstellt.

Folgendes musste ich tun:

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

Ich musste eine Variable erstellen, um die Antwort zu erfassen, die FileUtils.mkdir_p('dir_name')zurücksendet ... dann funktionierte alles wie ein Zauber!

Skplunkerin
quelle
macht keinen Sinn. Warum müssen Sie die Rückkehr fangen?
Tim Kretschmer
@huanson, ich musste die Rückkehr nicht fangen ... aber die Logik funktionierte nicht, bis ich sie erstellte response = FileUtils.mkdir_p('dir_name'). Wenn ich diese Variable nicht erstellt habe, FileUtils.mkdir_p('dir_name')nicht für mich gearbeitet habe ... oder zumindest ist das, woran ich mich erinnere, passiert (diese Antwort ist älter als 1 Jahr). Es würde mich nicht wundern, wenn eine neuere Version von Ruby dieses Problem behebt.
Skplunkerin
2

Wie wäre es mit Pathname?

require 'pathname'
some_path = Pathname("somedir/some_subdir/some-file.html")
some_path.dirname.mkdir_p
some_path.write(builder.to_html)
Eisensand
quelle
1
Es funktioniert mit some_path.dirname.mkpathanstelle vonsome_path.dirname.mkdir_p
Mauro Nidola
1
+1 ein mkpath. Auch wenn Sie nur das Verzeichnis und nicht den Pfad haben, ist dies nicht erforderlich dirname, z. B. Pfadname ("somedir / some_subdir"). Mkpath funktioniert auf die gleiche Weise.
Michael
1

In ähnlicher Weise (und abhängig von Ihrer Struktur) haben wir auf diese Weise gelöst, wo Screenshots gespeichert werden sollen:

In unserem env-Setup (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

Und in unseren 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?
Shell Bryson
quelle
1

Die einzige Lösung für die "Kernbibliothek" der Top-Antwort war unvollständig. Wenn Sie nur Kernbibliotheken verwenden möchten, verwenden Sie Folgendes:

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

Ich brauchte diese Lösung, weil das Abhängigkeitsjuwel von FileUtils, rmagick, die Bereitstellung meiner Rails-App auf Amazon Web Services verhinderte, da rmagick vom Paket libmagickwand-dev (Ubuntu) / imagemagick (OSX) abhängt, um ordnungsgemäß zu funktionieren.

CopyLeft
quelle