Comment créer un répertoire s'il n'en existe aucun en utilisant la classe File dans Ruby?

121

J'ai cette déclaration:

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

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

Ce que je veux, c'est que s'il n'y a pas de répertoire appelé somedirou some_subdirou les deux dans le chemin, je veux qu'il le crée automatiquement.

Comment puis je faire ça?

marcamillion
la source

Réponses:

154

Vous pouvez utiliser FileUtils pour créer de manière récursive des répertoires parents, s'ils ne sont pas déjà présents:

require 'fileutils'

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

Edit: Voici une solution utilisant uniquement les bibliothèques de base (réimplémentation de la roue, non recommandée)

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
Eurêka
la source
Ah d'accord. Je voulais dire le noyau, pas le stdlib. De toute façon, c'est bien. Cela marche. Merci!
marcamillion
1
J'ai ajouté une solution de base uniquement à ma réponse: sachez cependant que cela se réimplémente essentiellement FileUtils.mkdir_p(qui est la méthode dédiée à votre cas d'utilisation)
Eureka
57
Notez que cela FileUtils#mkdir_pfonctionne même si la hiérarchie de répertoires existe déjà (elle ne fait simplement rien), donc cette solution peut être compressée dans ce can one-liner plus une instruction require:FileUtils.mkdir_p(File.dirname(some_path))
Eureka
1
@JosephK - pour moi, cette erreur (trompeuse) EEXIST a fini par être un problème d'autorisation.
TomG
81

Pour ceux qui recherchent un moyen de créer un répertoire s'il n'existe pas , voici la solution simple:

require 'fileutils'

FileUtils.mkdir_p 'dir_name'

Basé sur le commentaire d'Eureka .

Andrey Mikhaylov - Lolmaus
la source
1
Ceci est le commentaire de @ Eureka - "Notez que FileUtils # mkdir_p fonctionne même si la hiérarchie des répertoires existe déjà (elle ne fait simplement rien), donc cette solution peut être compressée dans ce can one-liner plus une instruction require: FileUtils.mkdir_p(File.dirname(some_path))"
Darpan
23
directory_name = "name"
Dir.mkdir(directory_name) unless File.exists?(directory_name)
Licysca
la source
2
Vous pouvez rencontrer des conditions de concurrence en utilisant cette méthode, le répertoire pourrait être créé après File.exists? s'exécute mais avant que Dir.mkdir ne soit exécuté.
Matt Fenelon
4

Sur la base des autres réponses, rien ne s'est passé (n'a pas fonctionné). Il n'y a eu aucune erreur et aucun répertoire n'a été créé.

Voici ce que je devais faire:

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

J'avais besoin de créer une variable pour attraper la réponse qui FileUtils.mkdir_p('dir_name')renvoie ... alors tout a fonctionné comme un charme!

skplunkerin
la source
n'a pas de sens. pourquoi avez-vous besoin d'attraper le retour?
Tim Kretschmer
@huanson, je n'avais pas besoin d'attraper le retour ... mais la logique n'a fonctionné que lorsque j'ai créé response = FileUtils.mkdir_p('dir_name'). Si je n'ai pas créé cette variable, FileUtils.mkdir_p('dir_name')ne fonctionnait pas pour moi ... ou du moins c'est ce dont je me souviens qui s'est passé (cette réponse a plus d'un an). Je ne serais pas surpris si une version plus récente de Ruby résout ce problème.
skplunkerin le
2

Que diriez-vous d'utiliser Pathname?

require 'pathname'
some_path = Pathname("somedir/some_subdir/some-file.html")
some_path.dirname.mkdir_p
some_path.write(builder.to_html)
fer à repasser
la source
1
Cela fonctionne avec some_path.dirname.mkpathau lieu desome_path.dirname.mkdir_p
Mauro Nidola
1
+1 sur mkpath. De plus, si vous avez juste le répertoire et non le chemin, il n'y a pas besoin de dirname, par exemple Pathname ("somedir / some_subdir"). Mkpath fonctionnera de la même manière.
Michael
1

Dans le même esprit (et en fonction de votre structure), voici comment nous avons résolu où stocker les captures d'écran:

Dans notre configuration d'environnement (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

Et dans nos 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
la source
1

La seule solution de "bibliothèque principale" de la réponse supérieure était incomplète. Si vous souhaitez utiliser uniquement les bibliothèques principales, utilisez ce qui suit:

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

J'avais besoin de cette solution car le gemme de dépendance de FileUtils rmagick empêchait mon application Rails de se déployer sur Amazon Web Services car rmagick dépend du package libmagickwand-dev (Ubuntu) / imagemagick (OSX) pour fonctionner correctement.

CopyLeft
la source