Créer un répertoire s'il n'existe pas avec Ruby

156

J'essaye de créer un répertoire avec le code suivant:

Dir.mkdir("/Users/Luigi/Desktop/Survey_Final/Archived/Survey/test")
    unless File.exists?("/Users/Luigi/Desktop/Survey_Final/Archived/Survey/test")  

Cependant, je reçois cette erreur:

Aucun fichier ou répertoire de ce type - / Users / Luigi / Desktop / Survey_Final / Archived / Survey / test (Errno :: ENOENT)

Pourquoi ce répertoire n'est-il pas créé par l' Dir.mkdirinstruction ci-dessus?

Luigi
la source
4
File.exists?()fonctionne sur les fichiers et les dossiers. Il ne connaît pas la différence.
The Tin Man

Réponses:

263

Vous essayez probablement de créer des répertoires imbriqués. En supposant qu'il foon'existe pas, vous recevrez une no such file or directoryerreur pour:

Dir.mkdir 'foo/bar'
# => Errno::ENOENT: No such file or directory - 'foo/bar'

Pour créer des répertoires imbriqués à la fois, il FileUtilsest nécessaire:

require 'fileutils'
FileUtils.mkdir_p 'foo/bar'
# => ["foo/bar"]

Edit2: vous n'avez pas à utiliser FileUtils, vous pouvez faire un appel système (la mise à jour de @mu est un commentaire trop court):

> system 'mkdir', '-p', 'foo/bar' # worse version: system 'mkdir -p "foo/bar"'
=> true

Mais cela semble (du moins pour moi) une approche aussi pire que vous utilisez un «outil» externe qui peut être indisponible sur certains systèmes (bien que je puisse difficilement imaginer un système sans mkdir, mais qui sait).

zrl3dx
la source
5
system 'mkdir', '-p', 'foo/bar'serait une meilleure version de cet systemappel. Il n'y a pas besoin d'un processus shell supplémentaire ou du non-sens habituel des guillemets / échappements / injection qui accompagne la version à argument unique de system.
mu est trop court
6
systemse lancera /bin/shpour analyser la mkdir -p "foo/bar"chaîne, puis le shell s'exécutera /bin/mkdir. Vous faites donc un travail supplémentaire (créez la chaîne de commande, lancez-la /bin/shpour la séparer à nouveau) et une partie de ce travail supplémentaire vous laisse ouvert aux attaques par injection de shell (passez un peu de temps dans les avis CERT pour Ruby et vous verrez à quel point ce problème est).
mu est trop court
1
@muistooshort @ zrl3dx comment un systemappel est-il meilleur que de fileutilsnouveau? Je suis sous Windows et mkdir_pfonctionne très bien sans engendrer un sous-shell juste pour analyser mkdir -pce qui échouerait de toute façon. Heureux que ce fileutilssoit la première alternative dans la réponse.
TWiStErRob
1
@TWiStErRob: Relisez mes commentaires, je n'ai rien dit sur fileutilsou mkdir_p, tout ce que je dis, c'est que system command, arg1, arg2, ...c'est mieux que system command_with_arguments.
mu est trop court
3
@muistooshort ah, désolé, alors vous dites simplement qu'il y a une meilleure façon de faire la mauvaise option :)
TWiStErRob
71

Manière simple:

directory_name = "name"
Dir.mkdir(directory_name) unless File.exists?(directory_name)
Licysca
la source
8
On doit utiliser File.directory? plutôt que File.exists?
Florin Asăvoaie
4
Supposons qu'il existe un fichier normal portant le même nom. Vous ne pouvez pas créer de répertoire dans ce cas.
Mikołaj Rozwadowski
3
Cela crée également une condition de concurrence. Le fichier peut être créé après la vérification mais avant la création.
Don Reba
25

Un autre moyen simple:

Dir.mkdir('tmp/excel') unless Dir.exist?('tmp/excel')

Štefan Bartoš
la source
Si vous souhaitez créer des répertoires imbriqués, cela ne fonctionne pas. Par exemple, je voulais créer le répertoire suivant /home/jignesh/reports/testmais en utilisant cette solution soulevée RUBY (Errno::ENOENT), no such file or directory @ dir_s_mkdir. Donc, la solution fiable utiliseFileUtils.mkdir_p
Jignesh Gohel
-5

Que diriez-vous juste Dir.mkdir('dir') rescue nil?

Vidar
la source
3
Évitez d'utiliser rescuesous sa forme de modificateur.
Sebastian Palma
1
Voulez-vous expliquer pourquoi je devrais écrire 5 lignes de code au lieu d'une seule? J'aimerais vous voir essayer.
Vidar le
2
github.com/bbatsov/ruby-style-guide#no-rescue-modifiers regardez, s'il vous plaît
Sebastian Palma
1
Je l'ai déjà fait, et je suis totalement en désaccord, je pense que c'est idiot, alors peut-être pouvez-vous m'éclairer?
Vidar
6
Cela attraperait toute exception qui n'est pas ce que vous essayez de faire et dans une application du monde réel cacherait des problèmes rendant la maintenance plus difficile. En outre, ce n'est pas une bonne idée d'utiliser des exceptions comme conditionnelles, dans un sens matériel, elles fonctionnent beaucoup plus lentement (probablement pas vraiment un problème dans un langage moderne mais vous donne toujours l'air inexpérimenté en tant que codeur).
Ed_