Funcionalidad mkdir -p en Python

¿Hay alguna manera de obtener una funcionalidad similar a mkdir -p en el shell desde Python? Estoy buscando una solución que no sea una llamada al sistema. Estoy seguro de que el código tiene menos de 20 líneas y me pregunto si alguien ya lo ha escrito.

mkdir -p funcionalidad de la siguiente manera:

 import errno import os def mkdir_p(path): try: os.makedirs(path) except OSError as exc: # Python >2.5 if exc.errno == errno.EEXIST and os.path.isdir(path): pass else: raise 

Actualizar

Para Python ≥ 3.2, os.makedirs tiene un tercer argumento opcional exist_ok que, cuando es verdadero, habilita la funcionalidad mkdir -p – a menos que se proporcione el mode y el directorio existente tenga permisos diferentes a los previstos; en ese caso, OSError se plantea como anteriormente.

Actualización 2

Para Python ≥ 3.5, también hay pathlib.Path.mkdir :

 import pathlib pathlib.Path("/tmp/path/to/desired/directory").mkdir(parents=True, exist_ok=True) 

El parámetro exist_ok fue agregado en Python 3.5.

En Python> = 3.2, eso es

 os.makedirs(path, exist_ok=True) 

En versiones anteriores, usa la respuesta de @tzot .

Esto es más fácil que atrapar la excepción:

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

Descargo de responsabilidad Este enfoque requiere dos llamadas al sistema que son más susceptibles a las condiciones de carrera en ciertos entornos / condiciones. Si está escribiendo algo más sofisticado que un simple script desechable que se ejecuta en un entorno controlado, es mejor que vaya con la respuesta aceptada que solo requiere una llamada al sistema.

ACTUALIZACIÓN 2012-07-27

Estoy tentado de eliminar esta respuesta, pero creo que hay valor en el hilo de comentarios a continuación. Como tal, lo estoy convirtiendo a un wiki.

Recientemente, encontré este distutils.dir_util.mkpath :

 In [17]: from distutils.dir_util import mkpath In [18]: mkpath('./foo/bar') Out[18]: ['foo', 'foo/bar'] 

mkdir -p te da un error si el archivo ya existe:

 $ touch /tmp/foo $ mkdir -p /tmp/foo mkdir: cannot create directory `/tmp/foo': File exists 

Por lo tanto, un refinamiento de las sugerencias anteriores sería volver a raise la excepción si os.path.isdir devuelve False (cuando se comprueba errno.EEXIST ).

(Actualización) Véase también esta pregunta muy similar ; Estoy de acuerdo con la respuesta aceptada (y advertencias), excepto que recomendaría os.path.isdir lugar de os.path.exists .

(Actualización) Por una sugerencia en los comentarios, la función completa se vería así:

 import os def mkdirp(directory): if not os.path.isdir(directory): os.makedirs(directory) 

Como se mencionó en las otras soluciones, queremos poder golpear el sistema de archivos una vez mientras imitamos el comportamiento de mkdir -p . No creo que esto sea posible, pero deberíamos acercarnos lo más posible.

Código primero, explicación más tarde:

 import os import errno def mkdir_p(path): """ 'mkdir -p' in Python """ try: os.makedirs(path) except OSError as exc: # Python >2.5 if exc.errno == errno.EEXIST and os.path.isdir(path): pass else: raise 

Como los comentarios a la respuesta de @tzot indican que hay problemas para verificar si puede crear un directorio antes de que realmente lo cree: no puede saber si alguien ha cambiado el sistema de archivos mientras tanto. Eso también encaja con el estilo de Python de pedir perdón, no permiso.

Entonces, lo primero que debemos hacer es tratar de hacer el directorio, luego, si sale mal, averiguar por qué.

Como señala Jacob Gabrielson, uno de los casos que debemos buscar es el caso en el que ya existe un archivo en el que estamos intentando colocar el directorio.

Con mkdir -p :

 $ touch /tmp/foo $ mkdir -p /tmp/foo mkdir: cannot create directory '/tmp/foo': File exists 

El comportamiento análogo en Python sería generar una excepción.

Así que tenemos que resolver si este fue el caso. Desafortunadamente, no podemos. Obtenemos el mismo mensaje de error de makedirs si existe un directorio (bueno) o existe un archivo que impide la creación del directorio (incorrecto).

La única manera de averiguar qué sucedió es inspeccionar el sistema de archivos nuevamente para ver si hay un directorio allí. Si lo hay, devuélvelo en silencio, de lo contrario, levante la excepción.

El único problema es que el sistema de archivos puede estar en un estado diferente ahora que cuando se llamó a makedirs. por ejemplo: existía un archivo que causaba fallos en makedirs, pero ahora hay un directorio en su lugar. Eso realmente no importa mucho, porque la función solo se cerrará de forma silenciosa sin generar una excepción cuando, en el momento de la última llamada del sistema de archivos, existía el directorio.

Con Pathlib de la biblioteca estándar de python3:

 Path(mypath).mkdir(parents=True, exist_ok=True) 

Si los padres son verdaderos, los padres que faltan en este camino se crean según sea necesario; se crean con los permisos predeterminados sin tener en cuenta el modo (imitando el comando POSIX mkdir -p). Si exist_ok es falso (el valor predeterminado), se genera un FileExistsError si el directorio de destino ya existe.

Si exist_ok es verdadero, se ignorarán las excepciones FileExistsError (el mismo comportamiento que el comando POSIX mkdir -p), pero solo si el último componente de la ruta no es un archivo que no sea de directorio.

Cambiado en la versión 3.5: Se agregó el parámetro exist_ok.

Creo que la respuesta de Asa es esencialmente correcta, pero podría ampliarla un poco para que se parezca más a mkdir -p :

 import os def mkdir_path(path): if not os.access(path, os.F_OK): os.mkdirs(path) 

o

 import os import errno def mkdir_path(path): try: os.mkdirs(path) except os.error, e: if e.errno != errno.EEXIST: raise 

Ambos manejan el caso en el que la ruta ya existe en silencio, pero permiten que otros errores crezcan.

Declaración de funciones;

 import os def mkdir_p(filename): try: folder=os.path.dirname(filename) if not os.path.exists(folder): os.makedirs(folder) return True except: return False 

uso:

 filename = "./download/80c16ee665c8/upload/backup/mysql/2014-12-22/adclient_sql_2014-12-22-13-38.sql.gz" if (mkdir_p(filename): print "Created dir :%s" % (os.path.dirname(filename)) 

He tenido éxito con lo siguiente personalmente, pero mi función probablemente debería llamarse algo así como ‘asegurar que este directorio exista’:

 def mkdirRecursive(dirpath): import os if os.path.isdir(dirpath): return h,t = os.path.split(dirpath) # head/tail if not os.path.isdir(h): mkdirRecursive(h) os.mkdir(join(h,t)) # end mkdirRecursive 
 import os import tempfile path = tempfile.mktemp(dir=path) os.makedirs(path) os.rmdir(path) 
 import os from os.path import join as join_paths def mk_dir_recursive(dir_path): if os.path.isdir(dir_path): return h, t = os.path.split(dir_path) # head/tail if not os.path.isdir(h): mk_dir_recursive(h) new_path = join_paths(h, t) if not os.path.isdir(new_path): os.mkdir(new_path) 

basado en la respuesta de @Dave C pero con un error corregido donde ya existe parte del árbol