web-dev-qa-db-fra.com

Comment créer en toute sécurité un répertoire imbriqué en Python?

Quel est le moyen le plus élégant de vérifier si le répertoire dans lequel un fichier va être écrit existe, et si non, créez le répertoire en utilisant Python? Voici ce que j'ai essayé:

import os

file_path = "/my/directory/filename.txt"
directory = os.path.dirname(file_path)

try:
    os.stat(directory)
except:
    os.mkdir(directory)       

f = file(filename)

D'une manière ou d'une autre, j'ai raté os.path.exists (merci kanja, blair et douglas). Voici ce que j'ai maintenant:

def ensure_dir(file_path):
    directory = os.path.dirname(file_path)
    if not os.path.exists(directory):
        os.makedirs(directory)

Existe-t-il un drapeau pour "ouvert", ce qui rend cela se produit automatiquement?

3366
Parand

Je vois deux réponses de bonnes qualités, chacune avec un petit défaut, je vais donc donner mon point de vue:

Essayez os.path.exists et considérez os.makedirs pour la création.

import os
if not os.path.exists(directory):
    os.makedirs(directory)

Comme indiqué dans les commentaires et ailleurs, il existe une condition de concurrence critique: si le répertoire est créé entre les appels os.path.exists et os.makedirs, le os.makedirs échouera avec une OSError. Malheureusement, la capture de OSError et la poursuite de la couverture ne sont pas infaillibles, car la création du répertoire échouera en raison d'autres facteurs, tels que des autorisations insuffisantes, un disque complet, etc.

Une option serait d’intercepter la OSError et d’examiner le code d’erreur incorporé (voir Existe-t-il un moyen multi-plateforme d’obtenir des informations à partir de OSError de Python ):

import os, errno

try:
    os.makedirs(directory)
except OSError as e:
    if e.errno != errno.EEXIST:
        raise

Alternativement, il pourrait y avoir un deuxième os.path.exists, mais supposons qu'un autre ait créé le répertoire après le premier contrôle, puis l'a supprimé avant le second - nous pourrions toujours être dupes. 

En fonction de l'application, le danger lié aux opérations simultanées peut être supérieur ou inférieur au danger engendré par d'autres facteurs tels que les autorisations de fichiers. Le développeur devrait en savoir plus sur l’application en cours de développement et son environnement attendu avant de choisir une implémentation.

Les versions modernes de Python améliorent un peu ce code, à la fois en exposant FileExistsError (in 3.3 +) ...

try:
    os.makedirs("path/to/directory")
except FileExistsError:
    # directory already exists
    pass

... et en autorisant un argument de mot clé à os.makedirs appelé exist_ok (in 3.2+).

os.makedirs("path/to/directory", exist_ok=True)  # succeeds even if directory exists.
4171
Blair Conrad

En utilisant try except, le code d'erreur correct du module errno supprime la condition de concurrence critique et est multi-plateforme:

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

En d'autres termes, nous essayons de créer les répertoires, mais s'ils existent déjà, nous ignorons l'erreur. D'autre part, toute autre erreur est signalée. Par exemple, si vous créez préalablement un répertoire 'a' et en supprimez toutes les autorisations, vous obtenez une variable OSError générée avec errno.EACCES (autorisation refusée, erreur 13).

589
Heikki Toivonen

Je recommanderais personnellement que vous utilisiez os.path.isdir() pour tester au lieu de os.path.exists().

>>> os.path.exists('/tmp/dirname')
True
>>> os.path.exists('/tmp/dirname/filename.etc')
True
>>> os.path.isdir('/tmp/dirname/filename.etc')
False
>>> os.path.isdir('/tmp/fakedirname')
False

Si tu as:

>>> dir = raw_input(":: ")

Et une entrée utilisateur stupide:

:: /tmp/dirname/filename.etc

... Vous allez vous retrouver avec un répertoire nommé filename.etc lorsque vous passez cet argument à os.makedirs() si vous testez avec os.path.exists().

92
crimsonstone

Check os.makedirs : (Il s'assure que le chemin complet existe.)
Pour gérer le fait que le répertoire peut exister, récupérez OSError . (Si exist_ok est False (valeur par défaut), une OSError est déclenchée si le répertoire cible existe déjà.)

import os
try:
    os.makedirs('./path/to/somewhere')
except OSError:
    pass
66
Douglas Mayle

À partir de Python 3.5, pathlib.Path.mkdir a un drapeau exist_ok:

from pathlib import Path
path = Path('/my/directory/filename.txt')
path.parent.mkdir(parents=True, exist_ok=True) 
# path.parent ~ os.path.dirname(path)

Cela crée le répertoire de manière récursive et ne génère pas d'exception si le répertoire existe déjà.

(juste comme os.makedirs a un drapeau exist_ok à partir de python 3.2, par exemple os.makedirs(path, exist_ok=True))

47
hiro protagonist

Insights sur les spécificités de cette situation

Vous donnez un fichier particulier à un certain chemin et vous extrayez le répertoire du chemin de fichier. Ensuite, après vous être assuré que vous avez le répertoire, vous essayez d'ouvrir un fichier en lecture. Pour commenter ce code:

filename = "/my/directory/filename.txt"
dir = os.path.dirname(filename)

Nous voulons éviter d’écraser la fonction intégrée, dir. De plus, filepath ou peut-être fullfilepath est probablement un meilleur nom sémantique que filename, donc ce serait mieux écrit:

import os
filepath = '/my/directory/filename.txt'
directory = os.path.dirname(filepath)

Votre objectif final est d’ouvrir ce fichier, vous avez d’abord indiqué, pour écriture, mais vous vous approchez essentiellement de cet objectif (en fonction de votre code) comme ceci, qui ouvre le fichier pour reading :

if not os.path.exists(directory):
    os.makedirs(directory)
f = file(filename)

En supposant que l'ouverture pour la lecture

Pourquoi voudriez-vous créer un répertoire pour un fichier que vous espérez être là et que vous pourrez lire? 

Essayez simplement d'ouvrir le fichier.

with open(filepath) as my_file:
    do_stuff(my_file)

Si le répertoire ou le fichier n'existe pas, vous obtiendrez une IOError avec un numéro d'erreur associé: errno.ENOENT désignera le numéro d'erreur correct, quelle que soit votre plate-forme. Vous pouvez l'attraper si vous voulez, par exemple:

import errno
try:
    with open(filepath) as my_file:
        do_stuff(my_file)
except IOError as error:
    if error.errno == errno.ENOENT:
        print 'ignoring error because directory or file is not there'
    else:
        raise

En supposant que nous sommes ouverts à l'écriture

C'est probablement ce que vous voulez.

Dans ce cas, nous ne sommes probablement pas confrontés à des conditions de concurrence. Donc, faites comme vous étiez, mais notez que pour écrire, vous devez ouvrir avec le mode w (ou a à ajouter). Il est également recommandé d'utiliser le gestionnaire de contexte pour ouvrir des fichiers avec Python.

import os
if not os.path.exists(directory):
    os.makedirs(directory)
with open(filepath, 'w') as my_file:
    do_stuff(my_file)

Cependant, supposons que plusieurs processus Python tentent de placer toutes leurs données dans le même répertoire. Ensuite, nous pouvons avoir des conflits sur la création du répertoire. Dans ce cas, il est préférable de placer l'appel makedirs dans un bloc try-except.

import os
import errno
if not os.path.exists(directory):
    try:
        os.makedirs(directory)
    except OSError as error:
        if error.errno != errno.EEXIST:
            raise
with open(filepath, 'w') as my_file:
    do_stuff(my_file)
36
Aaron Hall

Essayez la fonction os.path.exists

if not os.path.exists(dir):
    os.mkdir(dir)
30
gone

J'ai mis ce qui suit. Ce n'est pas totalement infaillible cependant.

import os

dirname = 'create/me'

try:
    os.makedirs(dirname)
except OSError:
    if os.path.exists(dirname):
        # We are nearly safe
        pass
    else:
        # There was an error on creation, so make sure we know about it
        raise

Maintenant, comme je l'ai dit, ce n'est pas vraiment infaillible, car nous avons la possibilité de ne pas créer le répertoire, et un autre processus le créant pendant cette période.

27
Ali Afshar

Vérifier si un répertoire existe et le créer si nécessaire?

La réponse directe à cela est, en supposant une situation simple où vous ne vous attendez pas à ce que d'autres utilisateurs ou processus se moquent de votre annuaire:

if not os.path.exists(d):
    os.makedirs(d)

ou si la création du répertoire est soumise à des conditions de concurrence (c'est-à-dire si, après avoir vérifié que le chemin d'accès existe, quelque chose d'autre l'a déjà fait) procédez comme suit:

import errno
try:
    os.makedirs(d)
except OSError as exception:
    if exception.errno != errno.EEXIST:
        raise

Mais peut-être une meilleure solution consiste-t-elle à éviter le problème des conflits de ressources en utilisant des répertoires temporaires via tempfile :

import tempfile

d = tempfile.mkdtemp()

Voici l'essentiel de la doc en ligne:

mkdtemp(suffix='', prefix='tmp', dir=None)
    User-callable function to create and return a unique temporary
    directory.  The return value is the pathname of the directory.

    The directory is readable, writable, and searchable only by the
    creating user.

    Caller is responsible for deleting the directory when done with it.

Nouveau dans Python 3.5: pathlib.Path avec exist_ok

Il y a un nouvel objet Path (à partir de la version 3.4) avec beaucoup de méthodes que l'on voudrait utiliser avec des chemins, dont l'un est mkdir.

(Pour le contexte, je surveille mon représentant hebdomadaire avec un script. Voici les parties de code pertinentes du script qui me permettent d'éviter de toucher Stack Overflow plusieurs fois par jour pour les mêmes données.)

D'abord les importations en cause:

from pathlib import Path
import tempfile

Nous n'avons pas à traiter avec os.path.join maintenant - il suffit de joindre des parties de chemin avec un /:

directory = Path(tempfile.gettempdir()) / 'sodata'

Je vérifie ensuite que le répertoire existe - l'argument exist_ok apparaît dans Python 3.5:

directory.mkdir(exist_ok=True)

Voici la partie pertinente de documentation :

Si exist_ok est vrai, les exceptions FileExistsError seront ignorées (même comportement que la commande POSIX mkdir -p), mais uniquement si le dernier composant de chemin d'accès n'est pas un fichier non-répertoire existant.

Voici un peu plus de script - dans mon cas, je ne suis pas soumis à une condition de concurrence, je n'ai qu'un processus qui attend le répertoire (ou les fichiers contenus), et je n'ai rien à essayer de supprimer. le répertoire. 

todays_file = directory / str(datetime.datetime.utcnow().date())
if todays_file.exists():
    logger.info("todays_file exists: " + str(todays_file))
    df = pd.read_json(str(todays_file))

Les objets Path doivent être forcés à str avant que les autres API qui attendent des chemins str puissent les utiliser.

Peut-être que les pandas devraient être mis à jour pour accepter les instances de la classe de base abstraite, os.PathLike.

20
Aaron Hall

En Python 3.4, vous pouvez également utiliser le nouveau module pathlib :

from pathlib import Path
path = Path("/my/directory/filename.txt")
try:
    if not path.parent.exists():
        path.parent.mkdir(parents=True)
except OSError:
    # handle error; you can also catch specific errors like
    # FileExistsError and so on.
17
Antti Haapala

La documentation Python pertinente suggère l'utilisation du style de codage EAFP (Plus facile de demander pardon que de le permettre) . Cela signifie que le code

try:
    os.makedirs(path)
except OSError as exception:
    if exception.errno != errno.EEXIST:
        raise
    else:
        print "\nBE CAREFUL! Directory %s already exists." % path

est meilleur que l'alternative

if not os.path.exists(path):
    os.makedirs(path)
else:
    print "\nBE CAREFUL! Directory %s already exists." % path

La documentation suggère cela précisément à cause de la situation de concurrence critique décrite dans cette question. En outre, comme d’autres le mentionnent, le fait d’interroger une fois le système d’exploitation présente un avantage en termes de performances. Enfin, l'argument avancé, éventuellement, en faveur du second code dans certains cas - lorsque le développeur connaît l'environnement d'exécution de l'application - ne peut être préconisé que dans le cas particulier où le programme a mis en place un environnement privé. lui-même (et d'autres instances du même programme).

Même dans ce cas, c'est une mauvaise pratique et peut conduire à un long débogage inutile. Par exemple, le fait que nous ayons défini les autorisations pour un répertoire ne doit pas nous laisser l'impression que les autorisations sont définies de manière appropriée pour nos besoins. Un répertoire parent peut être monté avec d'autres autorisations. En général, un programme devrait toujours fonctionner correctement et le programmeur ne devrait pas s'attendre à un environnement spécifique.

12
kavadias

Dans Python3, os.makedirs prend en charge la définition de exist_ok. Le paramètre par défaut est False, ce qui signifie qu'une OSError sera déclenchée si le répertoire cible existe déjà. En définissant exist_ok sur True, OSError (le répertoire existe) sera ignoré et le répertoire ne sera pas créé.

os.makedirs(path,exist_ok=True)

Dans Python2, os.makedirs ne prend pas en charge le paramétrage exist_ok. Vous pouvez utiliser l'approche dans la réponse de heikki-toivonen :

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise
8
euccas

Vous pouvez utiliser mkpath

# Create a directory and any missing ancestor directories. 
# If the directory already exists, do nothing.

from distutils.dir_util import mkpath
mkpath("test")    

Notez qu'il créera également les répertoires ancêtres. 

Cela fonctionne pour Python 2 et 3.

8
Dennis Golomazov

J'utilise os.path.exists(), here est un script Python 3 qui peut être utilisé pour vérifier si un répertoire existe, en créer un s'il n'existe pas et le supprimer s'il existe (le cas échéant).

Il invite les utilisateurs à saisir le répertoire et peut être facilement modifié.

7
Michael Strobel

Pour une solution one-liner, vous pouvez utiliser IPython.utils.path.ensure_dir_exists():

from IPython.utils.path import ensure_dir_exists
ensure_dir_exists(dir)

À partir de documentation : Assurez-vous qu'un répertoire existe. S'il n'existe pas, essayez de le créer et protégez-vous contre une condition de concurrence critique si un autre processus en fait de même.

7
tashuhka

J'ai vu les réponses de Heikki Toivonen et A-B-B et j'ai pensé à cette variante.

import os
import errno

def make_sure_path_exists(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST or not os.path.isdir(path):
            raise
6
alissonmuller

Lorsque vous travaillez avec des entrées/sorties de fichiers, l’important est de

TOCTTOU (heure de vérification à l'heure d'utilisation)

Donc, faire une vérification avec if puis lire ou écrire plus tard peut aboutir à une exception d'E/S non gérée. La meilleure façon de le faire est:

try:
    os.makedirs(dir_path)
except OSError as e:
    if e.errno != errno.EEXIS:
        raise
6
SHAHS

Vous pouvez utiliser os.listdir pour cela:

import os
if 'dirName' in os.listdir('parentFolderPath')
    print('Directory Exists')
6
iPhynx

J'ai trouvé cette Q/A et j'étais initialement perplexe devant certains des échecs et des erreurs que je recevais. Je travaille dans Python 3 (v.3.5 dans un environnement virtuel Anaconda sur un système Arch Linux x86_64).

Considérez cette structure de répertoire:

└── output/         ## dir
   ├── corpus       ## file
   ├── corpus2/     ## dir
   └── subdir/      ## dir

Voici mes expériences/notes, qui clarifient les choses:

# ----------------------------------------------------------------------------
# [1] https://stackoverflow.com/questions/273192/how-can-i-create-a-directory-if-it-does-not-exist

import pathlib

""" Notes:
        1.  Include a trailing slash at the end of the directory path
            ("Method 1," below).
        2.  If a subdirectory in your intended path matches an existing file
            with same name, you will get the following error:
            "NotADirectoryError: [Errno 20] Not a directory:" ...
"""
# Uncomment and try each of these "out_dir" paths, singly:

# ----------------------------------------------------------------------------
# METHOD 1:
# Re-running does not overwrite existing directories and files; no errors.

# out_dir = 'output/corpus3'                ## no error but no dir created (missing tailing /)
# out_dir = 'output/corpus3/'               ## works
# out_dir = 'output/corpus3/doc1'           ## no error but no dir created (missing tailing /)
# out_dir = 'output/corpus3/doc1/'          ## works
# out_dir = 'output/corpus3/doc1/doc.txt'   ## no error but no file created (os.makedirs creates dir, not files!  ;-)
# out_dir = 'output/corpus2/tfidf/'         ## fails with "Errno 20" (existing file named "corpus2")
# out_dir = 'output/corpus3/tfidf/'         ## works
# out_dir = 'output/corpus3/a/b/c/d/'       ## works

# [2] https://docs.python.org/3/library/os.html#os.makedirs

# Uncomment these to run "Method 1":

#directory = os.path.dirname(out_dir)
#os.makedirs(directory, mode=0o777, exist_ok=True)

# ----------------------------------------------------------------------------
# METHOD 2:
# Re-running does not overwrite existing directories and files; no errors.

# out_dir = 'output/corpus3'                ## works
# out_dir = 'output/corpus3/'               ## works
# out_dir = 'output/corpus3/doc1'           ## works
# out_dir = 'output/corpus3/doc1/'          ## works
# out_dir = 'output/corpus3/doc1/doc.txt'   ## no error but creates a .../doc.txt./ dir
# out_dir = 'output/corpus2/tfidf/'         ## fails with "Errno 20" (existing file named "corpus2")
# out_dir = 'output/corpus3/tfidf/'         ## works
# out_dir = 'output/corpus3/a/b/c/d/'       ## works

# Uncomment these to run "Method 2":

#import os, errno
#try:
#       os.makedirs(out_dir)
#except OSError as e:
#       if e.errno != errno.EEXIST:
#               raise
# ----------------------------------------------------------------------------

Conclusion: à mon avis, la "méthode 2" est plus robuste.

[1] Comment créer un répertoire s'il n'existe pas?

[2] https://docs.python.org/3/library/os.html#os.makedirs

6
Victoria Stuart

Si vous considérez ce qui suit: 

os.path.isdir('/tmp/dirname')

signifie qu'un répertoire (chemin) existe ET est un répertoire. Donc, pour moi, cette façon fait ce dont j'ai besoin. Je peux donc m'assurer que c'est un dossier (pas un fichier) et qu'il existe.

3
Ralph Schwerdt

Utilisez la bibliothèque pathlib

import pathlib
mypath="/my/directory"
pathlib.Path(mypath).mkdir(parents=True, exist_ok=True) 
2

Appelez la fonction create_dir() au point d’entrée de votre programme/projet.

import os

def create_dir(directory):
    if not os.path.exists(directory):
        print('Creating Directory '+directory)
        os.makedirs(directory)

create_dir('Project directory')
2

Utilisez cette commande check et create dir

 if not os.path.isdir(test_img_dir):
     os.mkdir(str("./"+test_img_dir))
1

Vous devez définir le chemin complet avant de créer le répertoire:

import os,sys,inspect
import pathlib

currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
your_folder = currentdir + "/" + "your_folder"

if not os.path.exists(your_folder):
   pathlib.Path(your_folder).mkdir(parents=True, exist_ok=True)

Cela fonctionne pour moi et j'espère que cela fonctionnera pour vous aussi

0
Hussam Kurd
import os
if os.path.isfile(filename):
    print "file exists"
else:
    "Your code here"

Où votre code est ici utilisez la commande (tactile)

Cela vérifiera si le fichier est là sinon il le créera.

0
Evil Exists

Pourquoi ne pas utiliser le module de sous-processus s’il s’applique sur un ordinateur prenant en charge les langages Shell? Fonctionne sur python 2.7 et python 3.6

from subprocess import call
call(['mkdir', '-p', 'path1/path2/path3'])

Devrait faire l'affaire sur la plupart des systèmes.

0
Geoff Paul Bremner