¿Cómo creo un directorio si no existe ninguno usando la clase File en Ruby?

121

Tengo esta afirmación:

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

Dónde

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

Lo que quiero que suceda es, si no hay un directorio llamado somediro some_subdiro ambos en el camino, yo quiero que crean que automágicamente.

¿Cómo puedo hacer eso?

marcamillion
fuente

Respuestas:

154

Puede usar FileUtils para crear recursivamente directorios principales, si aún no están presentes:

require 'fileutils'

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

Editar: Aquí hay una solución que usa solo las bibliotecas principales (no se recomienda volver a implementar la rueda)

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
fuente
44
FileUtils está en el stdlib: ruby-doc.org/stdlib-1.9.3/libdoc/fileutils/rdoc/FileUtils.html
Eureka
Oh ok Me refería al núcleo, no al stdlib. De cualquier manera, está bien. Esto funciona. ¡Gracias!
marcamillion
1
Agregué una solución de solo núcleo a mi respuesta: Tenga en cuenta, sin embargo, que esencialmente se vuelve a implementar FileUtils.mkdir_p(que es el método dedicado a su caso de uso)
Eureka
57
Tenga en cuenta que FileUtils#mkdir_pfunciona incluso si la jerarquía de directorios ya existe (simplemente no hace nada), por lo que esta solución se puede comprimir en esta línea única más una declaración de requerimiento:FileUtils.mkdir_p(File.dirname(some_path))
Eureka
1
@JosephK: para mí, este error EEXIST (engañoso) terminó siendo un problema de permiso.
TomG
81

Para aquellos que buscan una manera de crear un directorio si no existe , aquí está la solución simple:

require 'fileutils'

FileUtils.mkdir_p 'dir_name'

Basado en el comentario de Eureka .

Andrey Mikhaylov - lolmaus
fuente
1
Este es el comentario de @ Eureka: "Tenga en cuenta que FileUtils # mkdir_p funciona incluso si la jerarquía de directorios ya existe (simplemente no hace nada), por lo que esta solución se puede comprimir en esta lata de una sola línea más una declaración de requerimiento: FileUtils.mkdir_p(File.dirname(some_path))"
Darpan
23
directory_name = "name"
Dir.mkdir(directory_name) unless File.exists?(directory_name)
Licysca
fuente
2
Puede encontrarse con condiciones de carrera utilizando este método, el directorio podría crearse después de File.exists? se ejecuta pero antes de que se ejecute Dir.mkdir.
Matt Fenelon el
4

Según las respuestas de los demás, no pasó nada (no funcionó). No hubo error y no se creó ningún directorio.

Esto es lo que necesitaba hacer:

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

Necesitaba crear una variable para captar la respuesta que FileUtils.mkdir_p('dir_name')envía de vuelta ... ¡entonces todo funcionó de maravilla!

skplunkerin
fuente
no tiene sentido ¿Por qué necesitas tomar la devolución?
Tim Kretschmer
@huanson, no necesitaba tomar el retorno ... pero la lógica no funcionó hasta que creé response = FileUtils.mkdir_p('dir_name'). Si no creé esta variable, FileUtils.mkdir_p('dir_name')no estaba funcionando para mí ... o al menos eso es lo que recuerdo que sucedió (esta respuesta tiene más de 1 año). No me sorprendería si una versión más nueva de Ruby corrige este problema.
skplunkerin
2

¿Qué tal el uso Pathname?

require 'pathname'
some_path = Pathname("somedir/some_subdir/some-file.html")
some_path.dirname.mkdir_p
some_path.write(builder.to_html)
hierro y
fuente
1
Funciona con en some_path.dirname.mkpathlugar desome_path.dirname.mkdir_p
Mauro Nidola
1
+1 en mkpath. Además, si solo tiene el directorio y no la ruta, no es necesario dirname, por ejemplo, Nombre de ruta ("somedir / some_subdir"). Mkpath funcionará de la misma manera.
Michael
1

En líneas similares (y dependiendo de su estructura), así es como resolvimos dónde almacenar capturas de pantalla:

En nuestra configuración 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

Y en nuestros 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
fuente
1

La única solución de la "biblioteca principal" de la respuesta superior estaba incompleta. Si solo desea usar bibliotecas principales, use lo siguiente:

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

Necesitaba esta solución porque la gema de dependencia de FileUtils, rmagick, impidió que mi aplicación Rails se implementara en Amazon Web Services, ya que rmagick depende del paquete libmagickwand-dev (Ubuntu) / imagemagick (OSX) para funcionar correctamente.

CopyLeft
fuente