cambiare - Come posso creare in modo sicuro una directory nidificata in Python?




directory python (17)

Controllare se esiste una directory e crearla se necessario?

La risposta diretta a questo è, assumendo una situazione semplice in cui non ci si aspetta che altri utenti o processi facciano casino con la propria directory:

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

o se fare la directory è soggetta a condizioni di competizione (cioè se dopo aver verificato che il percorso esiste, qualcos'altro potrebbe averlo già fatto) fai questo:

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

Ma forse un approccio ancora migliore è quello di eludere il problema del conflitto di risorse, utilizzando le directory temporanee tramite tempfile :

import tempfile

d = tempfile.mkdtemp()

Ecco gli elementi essenziali del documento online:

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.

Novità in Python 3.5: pathlib.Path con exist_ok

C'è un nuovo oggetto Path (a partire da 3.4) con molti metodi che si vorrebbe usare con i percorsi - uno dei quali è mkdir .

(Per il contesto, sto monitorando il mio rappresentante settimanale con uno script. Ecco le parti rilevanti del codice dallo script che mi consentono di evitare di colpire Stack Overflow più di una volta al giorno per gli stessi dati.)

Innanzitutto le importazioni rilevanti:

from pathlib import Path
import tempfile

Non dobbiamo occuparci ora di os.path.join - basta unire parti di percorsi con a / :

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

Quindi mi exist_ok la directory esista - l'argomento exist_ok presenta in Python 3.5:

directory.mkdir(exist_ok=True)

Ecco la parte rilevante della pathlib.Path.mkdir :

Se exist_ok è true, FileExistsError eccezioni FileExistsError verranno ignorate (stesso comportamento del comando POSIX mkdir -p ), ma solo se l'ultimo componente del percorso non è un file non di directory esistente.

Ecco un po 'più della sceneggiatura - nel mio caso, non sono soggetto a condizioni di gara, ho solo un processo che si aspetta che la directory (o i file contenuti) siano lì, e non ho nulla che tenti di rimuovere la directory.

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))

Path oggetti Path devono essere forzati a str prima di altre API che si aspettano che i percorsi str possano utilizzarli.

Forse Pandas dovrebbe essere aggiornato per accettare istanze della classe base astratta, os.PathLike .

Qual è il modo più elegante per verificare se la directory in cui un file verrà scritto esiste, e in caso contrario, creare la directory usando Python? Ecco cosa ho provato:

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)

In qualche modo, mi mancava os.path.exists (grazie kanja, Blair e Douglas). Questo è quello che ho adesso:

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

C'è una bandiera per "open", che lo fa accadere automaticamente?


Approfondimenti sulle specificità di questa situazione

Si assegna un determinato file in un determinato percorso e si estrae la directory dal percorso del file. Quindi, dopo essersi assicurati di avere la directory, si tenta di aprire un file per la lettura. Per commentare questo codice:

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

Vogliamo evitare di sovrascrivere la funzione builtin, dir . Inoltre, filepath o forse fullfilepath è probabilmente un nome semantico migliore del nome del filename quindi sarebbe meglio scrivere:

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

Il tuo obiettivo finale è quello di aprire questo file, inizialmente, per scrivere, ma in sostanza ti stai avvicinando a questo obiettivo (basato sul tuo codice) come questo, che apre il file per la lettura :

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

Supponendo l'apertura per la lettura

Perché dovresti creare una directory per un file che prevedi di essere lì ed essere in grado di leggere?

Basta provare ad aprire il file.

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

Se la directory o il file non sono presenti, verrà IOError un errore IOError con un numero di errore associato: errno.ENOENT indicherà il numero di errore corretto indipendentemente dalla piattaforma. Puoi prenderlo se vuoi, per esempio:

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

Supponendo che stiamo aprendo per scrivere

Questo è probabilmente quello che vuoi.

In questo caso, probabilmente non stiamo affrontando alcuna condizione di gara. Quindi fai come sei, ma nota che per scrivere, devi aprire con la modalità w (o a per aggiungere). È anche una best practice di Python utilizzare il gestore del contesto per aprire i file.

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

Tuttavia, diciamo che abbiamo diversi processi Python che tentano di inserire tutti i loro dati nella stessa directory. Quindi potremmo avere contese sulla creazione della directory. In tal caso è meglio avvolgere la chiamata dei makedirs in un blocco 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)

A partire da Python 3.5, pathlib.Path.mkdir ha un flag 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)

Questo crea in modo ricorsivo la directory e non genera un'eccezione se la directory esiste già.

(proprio come os.makedirs ottenuto un flag exists_ok partire da python 3.2).


Chiama la funzione create_dir() nel punto di ingresso del tuo programma / progetto.

import os

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

create_dir('Project directory')

Ho messo giù il seguente. Tuttavia non è totalmente infallibile.

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

Ora come ho detto, questo non è veramente infallibile, perché abbiamo la possibilità di non riuscire a creare la directory, e un altro processo che la crea durante quel periodo.


Ho trovato questo Q / A e inizialmente sono rimasto perplesso da alcuni dei fallimenti e degli errori che stavo ottenendo. Sto lavorando in Python 3 (v.3.5 in un ambiente virtuale Anaconda su un sistema Arch Linux x86_64).

Considera questa struttura di directory:

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

Ecco i miei esperimenti / note, che chiariscono le cose:

# ----------------------------------------------------------------------------
# [1] https://.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
# ----------------------------------------------------------------------------

Conclusione: a mio parere, "Metodo 2" è più robusto.

[1] Come posso creare una directory se non esiste?

[2] os.makedirs


In Python3 , os.makedirs supporta l'impostazione exist_ok . L'impostazione predefinita è False , il che significa che verrà sollevato un OSError se la directory di destinazione esiste già. Impostando exist_ok su True , OSError (directory esistente) verrà ignorato e la directory non verrà creata.

os.makedirs(path,exist_ok=True)

In Python2 , os.makedirs non supporta l'impostazione exist_ok . Puoi usare l'approccio nella risposta di 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

In Python 3.4 puoi anche utilizzare il nuovissimo modulo 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.

La relativa documentazione di Python suggerisce l'uso dello stile di codifica EAFP (più facile chiedere perdono che autorizzazione) . Questo significa che il codice

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

è meglio dell'alternativa

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

La documentazione suggerisce questo esattamente a causa delle condizioni di competizione discusse in questa domanda. Inoltre, come altri menzionano qui, c'è un vantaggio in termini di prestazioni nella query una volta invece del doppio del sistema operativo. Infine, l'argomentazione avanzata, potenzialmente, a favore del secondo codice in alcuni casi - quando lo sviluppatore conosce l'ambiente in cui è in esecuzione l'applicazione - può essere sostenuta solo nel caso particolare in cui il programma abbia creato un ambiente privato per stesso (e altre istanze dello stesso programma).

Anche in questo caso, questa è una cattiva pratica e può portare a lunghe debug inutili. Ad esempio, il fatto che impostiamo le autorizzazioni per una directory non dovrebbe lasciarci con le autorizzazioni di impressione sono impostate in modo appropriato per i nostri scopi. Una directory padre può essere montata con altre autorizzazioni. In generale, un programma dovrebbe sempre funzionare correttamente e il programmatore non dovrebbe aspettarsi un ambiente specifico.


Per una soluzione one-liner, è possibile utilizzare IPython.utils.path.ensure_dir_exists() :

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

Dalla documentation : assicurarsi che esista una directory. Se non esiste, prova a crearlo e proteggerlo contro una condizione di competizione se un altro processo sta facendo lo stesso.


Personalmente raccomanderei di usare os.path.isdir() per testare invece di 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

Se hai:

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

E un input utente folle:

:: /tmp/dirname/filename.etc

... Si finirà con una directory denominata filename.etc quando si passa quell'argomento a os.makedirs() se si prova con os.path.exists() .


Prova la funzione os.path.exists

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

Puoi usare os.listdir per questo:

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

Quando si lavora con I / O di file, la cosa importante da considerare è

TOCTTOU (tempo di controllo al momento dell'uso)

Quindi, fare un controllo con if e quindi leggere o scrivere più tardi può finire in un'eccezione I / O non gestita. Il modo migliore per farlo è:

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

Usa questo comando controlla e crea dir

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

Usando try except e il codice di errore corretto dal modulo errno si sbarazza delle condizioni della gara ed è multipiattaforma:

import os
import errno

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

In altre parole, proviamo a creare le directory, ma se esistono già ignoriamo l'errore. D'altra parte, viene segnalato qualsiasi altro errore. Ad esempio, se crei in precedenza dir 'a' e ne rimuovi tutte le autorizzazioni, otterrai un OSError generato con errno.EACCES (Autorizzazione negata, errore 13).


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

Dove il tuo codice è qui usa il comando (touch)

Questo controllerà se il file è lì se non lo è, allora lo creerà.







operating-system