Funcionalidad mkdir -p en Python [duplicado]
Pregunta
Esta pregunta ya tiene una respuesta aquí:
- ¿Cómo puedo crear un directorio anidado de forma segura? 25 respuestas
¿Existe una 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.
Solución
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 proporciona code> mode y el directorio existente tiene permisos diferentes a los previstos; en ese caso, OSError
se genera 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
se agregó en Python 3.5.
Otros consejos
En Python > = 3.2, eso es
os.makedirs(path, exist_ok=True)
En versiones anteriores, use 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
le 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 generar
la excepción si os.path.isdir
devuelve False
(al verificar para errno.EEXIST
).
(Actualizar) Consulte también esto pregunta muy similar ; Estoy de acuerdo con la respuesta aceptada (y advertencias), excepto que recomendaría os.path.isdir
en lugar de os.path.exists
.
(Actualización) Por una sugerencia en los comentarios, la función completa sería:
import os
def mkdirp(directory):
if not os.path.isdir(directory):
os.makedirs(directory)
Como se mencionó en las otras soluciones, queremos poder acceder al 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.
Primero el código, luego la explicación:
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 decir 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 que ya existe un archivo en el que estamos tratando de 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. Recibimos el mismo mensaje de error de makedirs si existe un directorio (bueno) o existe un archivo que impide la creación del directorio (malo).
La única forma de resolver lo que sucedió es inspeccionar el sistema de archivos nuevamente para ver si hay un directorio allí. Si lo hay, devuélvalo en silencio, de lo contrario, genere 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á en silencio sin generar una excepción cuando en el momento de la última llamada al 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 como necesario; Se crean con los permisos predeterminados sin tomar modo en cuenta (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, las excepciones FileExistsError serán ignoradas (igual como el comando POSIX mkdir -p), pero solo si la última ruta El componente 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 puedes extenderla un poco para que actúe más como mkdir -p
, ya sea:
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 donde la ruta ya existe de manera silenciosa, pero permiten que otros errores se disparen.
Declaración de función;
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 solucionado donde ya existe parte del árbol