funcionalidad mkdir-p en Python [duplicar]


Esta pregunta ya tiene una respuesta aquí:

Hay 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 es de menos de 20 líneas, y me pregunto si alguien ya ha escrito?

 626
Author: Adam Matan, 2009-03-01

12 answers

mkdir -p funcionalidad como sigue:

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 true, habilita la funcionalidad mkdir -p - a menos que mode se proporciona 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)

Se agregó el parámetro exist_ok en Python 3.5.

 886
Author: tzot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-12 12:52:07

En Python > = 3.2, eso es

os.makedirs(path, exist_ok=True)

En versiones anteriores, use la respuesta de@tzot.

 193
Author: Fred Foo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:54:59

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 bajo ciertos entornos/condiciones. Si está escribiendo algo más sofisticado que un simple script desechable que se ejecuta en un entorno controlado, es mejor ir con la respuesta aceptada que requiere solo una llamada al sistema.

ACTUALIZACIÓN 2012-07-27

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

 137
Author: Joe Holloway,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-14 15:11:16

Recientemente, encontré esto distutils.dir_util.mkpath :

In [17]: from distutils.dir_util import mkpath

In [18]: mkpath('./foo/bar')
Out[18]: ['foo', 'foo/bar']
 42
Author: auraham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-15 21:39:43

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

Así que un refinamiento a las sugerencias anteriores sería volver araise la excepción si os.path.isdir devuelve False (al comprobar errno.EEXIST).

(Actualización) Ver también esta 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.

(Update) Según una sugerencia en los comentarios, la función completa se vería como:

import os
def mkdirp(directory):
    if not os.path.isdir(directory):
        os.makedirs(directory) 
 16
Author: Jacob Gabrielson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-04 13:49:38

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 después:

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 indican los comentarios a la respuesta de @tzot, hay problemas al comprobar si se puede crear un directorio antes de crearlo: no se puede saber si alguien ha cambiado el sistema de archivos mientras tanto. Que también encaja con el estilo de Python de pedir perdón, no permiso.

Así que lo primero que debemos hacer es tratar de hacer el directorio, entonces si sale mal, averiguar por qué.

Como señala Jacob Gabrielson, uno de los casos que debemos buscar es el caso donde ya existe un archivo donde estamos tratando de poner 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 plantear una excepción.

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

La única manera de averiguar lo que sucedió es inspeccionar el sistema de archivos de nuevo para ver si hay un directorio allí. Si lo hay, entonces regrese silenciosamente, 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. eg: un archivo existió causando que makedirs fallara, pero ahora un directorio está en su lugar. Eso realmente no importa mucho, porque la función solo saldrá silenciosamente sin generar una excepción cuando en el momento de la última llamada al sistema de archivos el directorio existía.

 11
Author: c4m3lo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-08 08:31:24

Creo que la respuesta de Asa es esencialmente correcta, pero podrías extenderla un poco para actuar 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 en el que la ruta ya existe silenciosamente, pero dejan que otros errores aparezcan.

 10
Author: davidavr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-02 01:47:26

Con Pathlib de la biblioteca estándar python3:

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

Si padres es verdadero, cualquier padre faltante de este camino se crea como necesarios; se crean con los permisos predeterminados sin tomar modo en cuenta (imitando el comando POSIX mkdir-p). Si exist_ok es false (el valor predeterminado), se genera un FileExistsError si el directorio de destino ya existe.

Si exist_ok es true, las excepciones de FileExistsError serán ignoradas (same comportamiento como el POSIX mkdir-p), pero solo si la última ruta el componente no es un archivo existente que no sea de directorio.

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

 10
Author: Ali SAID OMAR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-16 07:08:53

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))
 5
Author: Guray Celik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-22 22:06:56
import os
import tempfile

path = tempfile.mktemp(dir=path)
os.makedirs(path)
os.rmdir(path)
 2
Author: pixelbeat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-06 09:47:43

He tenido éxito con lo siguiente personalmente, pero mi función probablemente debería llamarse algo 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
 2
Author: Dave C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-15 02:28:07
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 parte del árbol ya existe

 0
Author: Dave00Galloway,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-11 15:35:12