Come creo una directory se non ne esiste nessuna utilizzando la classe File in Ruby?


121

Ho questa dichiarazione:

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

Dove

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

Quello che voglio che accada è che, se non ci sono directory chiamate somediro some_subdiro entrambe nel percorso, voglio che la crei automaticamente.

Come lo posso fare?

Risposte:


154

È possibile utilizzare FileUtils per creare in modo ricorsivo directory padre, se non sono già presenti:

require 'fileutils'

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

Modifica: ecco una soluzione che utilizza solo le librerie di base (reimplementazione della ruota, non consigliato)

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


Oh va bene. Intendevo il core, non lo stdlib. Ad ogni modo, va bene. Funziona. Grazie!
marcamillion,

1
Ho aggiunto una soluzione solo core alla mia risposta: tieni presente, tuttavia, che essenzialmente reimplementa FileUtils.mkdir_p(che è il metodo dedicato al tuo caso d'uso)
Eureka,

57
Nota che FileUtils#mkdir_pfunziona anche se la gerarchia di directory esiste già (semplicemente non fa nulla), quindi questa soluzione può essere compressa in questo può una riga più un'istruzione require:FileUtils.mkdir_p(File.dirname(some_path))
Eureka,

1
@JosephK - per me questo errore EEXIST (fuorviante) è finito per essere un problema di autorizzazione.
TomG

81

Per chi cerca un modo per creare una directory se non esiste , ecco la semplice soluzione:

require 'fileutils'

FileUtils.mkdir_p 'dir_name'

Basato sul commento di Eureka .


1
Questo è il commento di @ Eureka - "Nota che FileUtils # mkdir_p funziona anche se la gerarchia di directory esiste già (semplicemente non fa nulla), quindi questa soluzione può essere compressa in questo modo one-liner più un'istruzione require: FileUtils.mkdir_p(File.dirname(some_path))"
Darpan

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

2
Potresti incorrere in condizioni di competizione usando questo metodo, la directory potrebbe essere creata dopo che File.exists? viene eseguito ma prima che Dir.mkdir venga eseguito.
Matt Fenelon

4

Sulla base delle altre risposte, non è successo niente (non ha funzionato). Non si sono verificati errori e non è stata creata alcuna directory.

Ecco cosa dovevo fare:

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

Avevo bisogno di creare una variabile per catturare la risposta che FileUtils.mkdir_p('dir_name')manda indietro ... poi tutto ha funzionato a meraviglia!


non ha senso. perché hai bisogno di prendere il ritorno?
Tim Kretschmer

@huanson, non avevo bisogno di prendere il ritorno ... ma la logica non ha funzionato fino a quando non l'ho creata response = FileUtils.mkdir_p('dir_name'). Se non ho creato questa variabile, FileUtils.mkdir_p('dir_name')non funzionava per me ... o almeno è quello che ricordo è successo (questa risposta ha più di 1 anno). Non sarei sorpreso se una versione più recente di Ruby risolvesse questo problema.
skplunkerin

2

Che ne dici di usare Pathname?

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

1
Funziona con some_path.dirname.mkpathinvece disome_path.dirname.mkdir_p
Mauro Nidola

1
+1 su mkpath. Inoltre, se hai solo la directory e non il percorso, non è necessario dirname, ad esempio, Pathname ("somedir / some_subdir") mkpath funzionerà allo stesso modo.
Michael

1

Seguendo linee simili (e in base alla tua struttura), ecco come abbiamo risolto dove memorizzare gli screenshot:

Nella nostra configurazione 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

E nel nostro 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

L'unica soluzione della "libreria principale" della risposta principale era incompleta. Se desideri utilizzare solo le librerie di base, utilizza quanto segue:

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

Avevo bisogno di questa soluzione perché la dipendenza gem rmagick di FileUtils impediva la distribuzione della mia app Rails su Amazon Web Services poiché rmagick dipende dal pacchetto libmagickwand-dev (Ubuntu) / imagemagick (OSX) per funzionare correttamente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.