¿Cómo puedo crear un árbol de directorios en C ++ / Linux?

109

Quiero una forma fácil de crear varios directorios en C ++ / Linux.

Por ejemplo, quiero guardar un archivo lola.file en el directorio:

/tmp/a/b/c

pero si los directorios no están ahí, quiero que se creen automágicamente. Un ejemplo práctico sería perfecto.

Lipis
fuente
C ++ no tiene funciones integradas para crear directorios y árboles per se . Tendrá que usar C y llamadas al sistema o una biblioteca externa como Boost. C y las llamadas al sistema dependerán de la plataforma.
jww
6
@noloader Muchas gracias hombre ... pero creo que después de 4 años obtuve mi respuesta, como puedes ver a continuación, de 13 formas diferentes ...
Lipis
Sí, me sorprendió que nadie dijera explícitamente que no puede hacerlo en C ++ (suponiendo que quisiera un método portátil en C ++ que funcionara en Linux). Pero probablemente lo sabías;). Sin embargo, hubo muchas buenas sugerencias para el código C no portátil.
jww
¿Qué es "C ++ / Linux"?
Lightness Races in Orbit
3
@LightnessRacesinOrbit Son mis años universitarios en C ++ en Linux :)
Lipis

Respuestas:

59

Con C ++ 17 o posterior, existe el encabezado estándar <filesystem>con función std::filesystem::create_directories que debe usarse en los programas modernos de C ++. Sin embargo, las funciones estándar de C ++ no tienen el argumento de permisos explícitos (modo) específicos de POSIX.

Sin embargo, aquí hay una función de C que se puede compilar con compiladores de C ++.

/*
@(#)File:           mkpath.c
@(#)Purpose:        Create all directories in path
@(#)Author:         J Leffler
@(#)Copyright:      (C) JLSS 1990-2020
@(#)Derivation:     mkpath.c 1.16 2020/06/19 15:08:10
*/

/*TABSTOP=4*/

#include "posixver.h"
#include "mkpath.h"
#include "emalloc.h"

#include <errno.h>
#include <string.h>
/* "sysstat.h" == <sys/stat.h> with fixup for (old) Windows - inc mode_t */
#include "sysstat.h"

typedef struct stat Stat;

static int do_mkdir(const char *path, mode_t mode)
{
    Stat            st;
    int             status = 0;

    if (stat(path, &st) != 0)
    {
        /* Directory does not exist. EEXIST for race condition */
        if (mkdir(path, mode) != 0 && errno != EEXIST)
            status = -1;
    }
    else if (!S_ISDIR(st.st_mode))
    {
        errno = ENOTDIR;
        status = -1;
    }

    return(status);
}

/**
** mkpath - ensure all directories in path exist
** Algorithm takes the pessimistic view and works top-down to ensure
** each directory in path exists, rather than optimistically creating
** the last element and working backwards.
*/
int mkpath(const char *path, mode_t mode)
{
    char           *pp;
    char           *sp;
    int             status;
    char           *copypath = STRDUP(path);

    status = 0;
    pp = copypath;
    while (status == 0 && (sp = strchr(pp, '/')) != 0)
    {
        if (sp != pp)
        {
            /* Neither root nor double slash in path */
            *sp = '\0';
            status = do_mkdir(copypath, mode);
            *sp = '/';
        }
        pp = sp + 1;
    }
    if (status == 0)
        status = do_mkdir(path, mode);
    FREE(copypath);
    return (status);
}

#ifdef TEST

#include <stdio.h>
#include <unistd.h>

/*
** Stress test with parallel running of mkpath() function.
** Before the EEXIST test, code would fail.
** With the EEXIST test, code does not fail.
**
** Test shell script
** PREFIX=mkpath.$$
** NAME=./$PREFIX/sa/32/ad/13/23/13/12/13/sd/ds/ww/qq/ss/dd/zz/xx/dd/rr/ff/ff/ss/ss/ss/ss/ss/ss/ss/ss
** : ${MKPATH:=mkpath}
** ./$MKPATH $NAME &
** [...repeat a dozen times or so...]
** ./$MKPATH $NAME &
** wait
** rm -fr ./$PREFIX/
*/

int main(int argc, char **argv)
{
    int             i;

    for (i = 1; i < argc; i++)
    {
        for (int j = 0; j < 20; j++)
        {
            if (fork() == 0)
            {
                int rc = mkpath(argv[i], 0777);
                if (rc != 0)
                    fprintf(stderr, "%d: failed to create (%d: %s): %s\n",
                            (int)getpid(), errno, strerror(errno), argv[i]);
                exit(rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
            }
        }
        int status;
        int fail = 0;
        while (wait(&status) != -1)
        {
            if (WEXITSTATUS(status) != 0)
                fail = 1;
        }
        if (fail == 0)
            printf("created: %s\n", argv[i]);
    }
    return(0);
}

#endif /* TEST */

Las macros STRDUP()y FREE()son versiones de comprobación de errores de strdup()y free(), declaradas en emalloc.h(e implementadas en emalloc.cy estrdup.c). El "sysstat.h"encabezado trata con versiones rotas <sys/stat.h> y puede ser reemplazado por <sys/stat.h>en sistemas Unix modernos (pero hubo muchos problemas en 1990). Y "mkpath.h"declara mkpath().

El cambio entre v1.12 (versión original de la respuesta) y v1.13 (versión enmendada de la respuesta) fue la prueba para EEXISTin do_mkdir(). Esto fue señalado como necesario por Switch , gracias, Switch. El código de prueba se actualizó y reprodujo el problema en una MacBook Pro (Intel Core i7 de 2.3GHz, con Mac OS X 10.7.4) y sugiere que el problema se solucionó en la revisión (pero las pruebas solo pueden mostrar la presencia de errores , nunca su ausencia). El código que se muestra ahora es v1.16; se han realizado cambios cosméticos o administrativos desde v1.13 (como el uso en mkpath.hlugar de jlss.he incluir <unistd.h>incondicionalmente en el código de prueba únicamente). Es razonable argumentar que "sysstat.h"debería reemplazarse por a <sys/stat.h>menos que tenga un sistema inusualmente recalcitrante.

(Por la presente, se le otorga permiso para usar este código para cualquier propósito con atribución).

Este código está disponible en mi repositorio SOQ (Preguntas de desbordamiento de pila) en GitHub como archivos mkpath.cy mkpath.h(etc.) en el subdirectorio src / so-0067-5039 .

Jonathan Leffler
fuente
2
Seguramente es más rápido que el sistema. El sistema tiene muchos gastos generales involucrados. Básicamente, el proceso debe bifurcarse, luego deben cargarse al menos dos binarios (probablemente uno ya esté en la caché), uno de los cuales será otro bifurcación del otro, ...
ypnos
1
Lo olvidé: ¡Y luego "mkdir -p" hará al menos lo mismo que el código publicado arriba!
ypnos
7
Hay una condición de carrera sutil en este código que realmente alcancé. Solo sucede cuando varios programas se inician simultáneamente y hacen la misma ruta de carpeta. La solución es agregar if (errno != EEXIST) { status = -1; }cuando mkdir falla.
Cambio del
2
@Switch: Gracias. Ese es el problema de usar stat()antes mkdir(); es un problema TOCTOU (tiempo de comprobación, tiempo de uso). Intenté hacerle cosquillas al error con un script de shell que ejecuta 13 procesos en segundo plano creando la misma ruta de 29 elementos, y no pude encontrarlo. Luego pirateé el programa de prueba para bifurcar 20 veces y hacer que cada niño lo intentara, y eso logró solucionar el error. El código fijo tendrá if (mkdir(path, mode) != 0 && errno != EEXIST) status = -1;. Eso no muestra el error.
Jonathan Leffler
2
@DavidMerinos: son encabezados ( jlss.h, emalloc.h), no bibliotecas. Sin embargo, el código está disponible en mi SOQ (Preguntas desbordamiento de pila) repositorio en GitHub como archivos jlss.h, emalloc.cy emalloc.hen el / libsoq src subdirectorio. Usted necesitará posixver.htambién, y algunos otros ( debug.h, stderr.c, stderr.h- creo que eso es todo, pero lo que necesita debe estar todos en ese directorio).
Jonathan Leffler
157

Fácil con Boost.Filesystem: create_directories

#include <boost/filesystem.hpp>
//...
boost::filesystem::create_directories("/tmp/a/b/c");

Devuelve: truesi se creó un nuevo directorio, en caso contrario false.

Benoît
fuente
9
Bueno, la mayoría de las bibliotecas de impulso son solo de encabezado, lo que significa que no hay gastos adicionales además de lo que usa. Sin embargo, en el caso de Boost.Filesystem, requiere compilación. En mi disco, la biblioteca compilada pesa ~ 60KB.
Benoît
1
@Lipis: precisa cuál es tu sistema integrado. Creo que debería estar disponible en casi todas las distribuciones de Linux.
Benoît
4
Con respecto a los compiladores de C ++ 11 mencionados por @danijar, el comentario aquí lo dejó más claro: The <filesystem> header is not part of C++11; it is a proposal for C++ TR2 based on the Boost.Filesystem library. Visual C++ 2012 includes an implementation of the proposed library.
Chunliang Lyu
5
boost :: filesystem no es solo de encabezado: stackoverflow.com/questions/13604090/…
ftvs
2
En mi humilde opinión: En cualquier proyecto mío que esté destinado a hacer algo significativo y resistir la prueba del tiempo, vale la pena compilarlo para tener un conjunto de herramientas estandarizadas increíblemente útiles y poderosas como boost. Mucho de él ya se ha abierto camino en C ++ estándar, sin duda con más por venir. Pruébalo, apégate a él, te beneficiarás si tienes necesidades más que triviales y no quieres reinventar la rueda. :-)
moodboom
42
system("mkdir -p /tmp/a/b/c")

es la forma más corta en la que puedo pensar (en términos de la longitud del código, no necesariamente el tiempo de ejecución).

No es multiplataforma, pero funcionará en Linux.

Cristóbal
fuente
1
SI va a dar la solución como un comando de shell, sería bueno mencionar system (3)
dmckee --- ex-moderator kitten
26
#include <sys/types.h>
#include <sys/stat.h>

int status;
...
status = mkdir("/tmp/a/b/c", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);

De aqui . Puede que tenga que hacer mkdirs separados para / tmp, / tmp / a, / tmp / a / b / y luego / tmp / a / b / c porque no hay un equivalente de la marca -p en la api de C. Asegúrese de ignorar el error EEXISTS mientras realiza los de nivel superior.

Paul Tomblin
fuente
Dato curioso: al menos Solaris y HP / UX tienen mkdirp (), aunque claramente no es óptimo para la portabilidad.
Martin Carpenter
ese es el punto ... que no quiero llamar a todas estas funciones por separado.
Lipis
Llamar a mkdir varias veces será mucho más rápido que llamar al sistema una vez.
Paul Tomblin
No entiendo lo que está sugiriendo: ¿llamar a mkdir 4 veces con diferentes argumentos? ("/tmp/",...), ("/tmp/a/",...), ("/tmp/a/b/",...),("/tmp/a/b/c/",...)
Antonio
1
Nuevamente, es bastante trivial hacer la misma llamada tres veces. El punto es dar a la gente suficiente información para que puedan escribir el código, no escribir el código por ellos.
Paul Tomblin
25

Aquí está mi ejemplo de código (funciona tanto para Windows como para Linux):

#include <iostream>
#include <string>
#include <sys/stat.h> // stat
#include <errno.h>    // errno, ENOENT, EEXIST
#if defined(_WIN32)
#include <direct.h>   // _mkdir
#endif

bool isDirExist(const std::string& path)
{
#if defined(_WIN32)
    struct _stat info;
    if (_stat(path.c_str(), &info) != 0)
    {
        return false;
    }
    return (info.st_mode & _S_IFDIR) != 0;
#else 
    struct stat info;
    if (stat(path.c_str(), &info) != 0)
    {
        return false;
    }
    return (info.st_mode & S_IFDIR) != 0;
#endif
}

bool makePath(const std::string& path)
{
#if defined(_WIN32)
    int ret = _mkdir(path.c_str());
#else
    mode_t mode = 0755;
    int ret = mkdir(path.c_str(), mode);
#endif
    if (ret == 0)
        return true;

    switch (errno)
    {
    case ENOENT:
        // parent didn't exist, try to create it
        {
            int pos = path.find_last_of('/');
            if (pos == std::string::npos)
#if defined(_WIN32)
                pos = path.find_last_of('\\');
            if (pos == std::string::npos)
#endif
                return false;
            if (!makePath( path.substr(0, pos) ))
                return false;
        }
        // now, try to create again
#if defined(_WIN32)
        return 0 == _mkdir(path.c_str());
#else 
        return 0 == mkdir(path.c_str(), mode);
#endif

    case EEXIST:
        // done!
        return isDirExist(path);

    default:
        return false;
    }
}

int main(int argc, char* ARGV[])
{
    for (int i=1; i<argc; i++)
    {
        std::cout << "creating " << ARGV[i] << " ... " << (makePath(ARGV[i]) ? "OK" : "failed") << std::endl;
    }
    return 0;
}

Uso:

$ makePath 1/2 folderA/folderB/folderC
creating 1/2 ... OK
creating folderA/folderB/folderC ... OK
Maxim Suslov
fuente
¡Apoyo ese comentario! No es (sorprendentemente) una tarea trivial encontrar una forma portátil de C ++ para crear un directorio. Esta respuesta necesita más votos a favor.
Manuel Lafond
1
En Windows, isDirExist no funciona si el carácter final es una barra invertida. Siempre devuelve falso. Tengo que modificar el código a: std :: string dirPath (ruta); while ('\\' == * dirPath.rbegin ()) dirPath.pop_back (); ... y luego, por supuesto, pase dirPath.c_str () en la llamada a _stat.
MiloDC
La API de Windows tiene "nombres no ANSI para compatibilidad" para stat(relacionados con __STDC__) que no es necesario realizar la prueba del precompilador.
Sandburg
18

Cabe señalar que a partir de la interfaz del sistema de archivos C ++ 17 es parte de la biblioteca estándar. Esto significa que uno puede tener lo siguiente para crear directorios:

#include <filesystem>

std::filesystem::create_directories("/a/b/c/d")

Más información aquí: https://en.cppreference.com/w/cpp/filesystem/create_directory

Además, con gcc, es necesario "-std = c ++ 17" para CFLAGS. Y "-lstdc ++ fs" a LDLIBS. Esto último potencialmente no será necesario en el futuro.

mcsim
fuente
También debería funcionar con lo suficientemente nuevo como Visual C ++ y "/ std: c ++ latest". Ver: blogs.msdn.microsoft.com/vcblog/2018/05/07/… y developercommunity.visualstudio.com/content/problem/296680/…
Ron Burk
9

Esto es similar al anterior, pero funciona hacia adelante a través de la cadena en lugar de recursivamente hacia atrás. Deja errno con el valor correcto para la última falla. Si hay una barra inclinada, hay un tiempo extra a través del bucle que podría haberse evitado mediante un find_first_of () fuera del bucle o detectando el / inicial y estableciendo pre en 1. La eficiencia es la misma ya sea que nos configure un primer ciclo o una llamada previa al ciclo, y la complejidad sería (ligeramente) mayor cuando se usa la llamada previa al ciclo.

#include <iostream>
#include <string>
#include <sys/stat.h>

int
mkpath(std::string s,mode_t mode)
{
    size_t pos=0;
    std::string dir;
    int mdret;

    if(s[s.size()-1]!='/'){
        // force trailing / so we can handle everything in loop
        s+='/';
    }

    while((pos=s.find_first_of('/',pos))!=std::string::npos){
        dir=s.substr(0,pos++);
        if(dir.size()==0) continue; // if leading / first time is 0 length
        if((mdret=mkdir(dir.c_str(),mode)) && errno!=EEXIST){
            return mdret;
        }
    }
    return mdret;
}

int main()
{
    int mkdirretval;
    mkdirretval=mkpath("./foo/bar",0755);
    std::cout << mkdirretval << '\n';

}
phorgan1
fuente
7

Dijiste "C ++" pero todos aquí parecen estar pensando "Bash shell".

Consulte el código fuente de GNU mkdir; entonces puede ver cómo implementar los comandos de shell en C ++.

Jason Cohen
fuente
Bueno, el sistema ("mkdir ...") debería funcionar en linux. Sin embargo, no es multiplataforma.
ChristopheD
Secundo lo que dice @MartinCarpenter
Joshua Hedges
6
bool mkpath( std::string path )
{
    bool bSuccess = false;
    int nRC = ::mkdir( path.c_str(), 0775 );
    if( nRC == -1 )
    {
        switch( errno )
        {
            case ENOENT:
                //parent didn't exist, try to create it
                if( mkpath( path.substr(0, path.find_last_of('/')) ) )
                    //Now, try to create again.
                    bSuccess = 0 == ::mkdir( path.c_str(), 0775 );
                else
                    bSuccess = false;
                break;
            case EEXIST:
                //Done!
                bSuccess = true;
                break;
            default:
                bSuccess = false;
                break;
        }
    }
    else
        bSuccess = true;
    return bSuccess;
}
marca
fuente
¡es la mejor solución para mí! ¡Gracias!)))
neo
tal vez sea una pregunta de volcado, pero ¿qué es el prefijo "::" antes de mkdir?
Rayee Roded
1
Encontró la respuesta, el :: garantiza que la resolución se produzca desde el espacio de nombres global stackoverflow.com/questions/4269034/…
Rayee Roded
4

Así que necesito mkdirp()hoy, y encontré las soluciones en esta página demasiado complicadas. Por lo tanto, escribí un fragmento bastante corto, que se puede copiar fácilmente para otros que se encuentren con este hilo y se pregunten por qué necesitamos tantas líneas de código.

mkdirp.h

#ifndef MKDIRP_H
#define MKDIRP_H

#include <sys/stat.h>

#define DEFAULT_MODE      S_IRWXU | S_IRGRP |  S_IXGRP | S_IROTH | S_IXOTH

/** Utility function to create directory tree */
bool mkdirp(const char* path, mode_t mode = DEFAULT_MODE);

#endif // MKDIRP_H

mkdirp.cpp

#include <errno.h>

bool mkdirp(const char* path, mode_t mode) {
  // const cast for hack
  char* p = const_cast<char*>(path);

  // Do mkdir for each slash until end of string or error
  while (*p != '\0') {
    // Skip first character
    p++;

    // Find first slash or end
    while(*p != '\0' && *p != '/') p++;

    // Remember value from p
    char v = *p;

    // Write end of string at p
    *p = '\0';

    // Create folder from path to '\0' inserted at p
    if(mkdir(path, mode) == -1 && errno != EEXIST) {
      *p = v;
      return false;
    }

    // Restore path to it's former glory
    *p = v;
  }

  return true;
}

Si no te gusta la conversión constante y la modificación temporal de la cadena, haz una strdup()y free()luego.

jonasfj
fuente
Publicado en una esencia también, así que no me olvido de dónde lo pongo la próxima vez que lo necesite :) gist.github.com/jonasfj/7797272
jonasfj
2
Es malo intentar modificar una cadena que se pasa como constante. Aparte de todo lo demás, es probable que provoque fallas dramáticas si alguna vez se le pasa una cadena literal.
Jonathan Leffler
2
Perfectamente correcto ... esto es realmente malo ... probablemente strcpy lo haría mejor ...
jonasfj
3

Dado que esta publicación ocupa un lugar destacado en Google para "Crear árbol de directorios", voy a publicar una respuesta que funcionará para Windows; esto funcionará usando la API Win32 compilada para UNICODE o MBCS. Esto se transfirió del código de Mark anterior.

Dado que estamos trabajando en Windows, los separadores de directorio son barras invertidas, no barras diagonales. Si prefiere tener barras diagonales, cambie '\\'a'/'

Funcionará con:

c:\foo\bar\hello\world

y

c:\foo\bar\hellp\world\

(es decir, no necesita barra inclinada final, por lo que no tiene que buscarla).

Antes de decir "Simplemente use SHCreateDirectoryEx () en Windows", tenga en cuenta que SHCreateDirectoryEx () está obsoleto y podría eliminarse en cualquier momento de futuras versiones de Windows.

bool CreateDirectoryTree(LPCTSTR szPathTree, LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL){
    bool bSuccess = false;
    const BOOL bCD = CreateDirectory(szPathTree, lpSecurityAttributes);
    DWORD dwLastError = 0;
    if(!bCD){
        dwLastError = GetLastError();
    }else{
        return true;
    }
    switch(dwLastError){
        case ERROR_ALREADY_EXISTS:
            bSuccess = true;
            break;
        case ERROR_PATH_NOT_FOUND:
            {
                TCHAR szPrev[MAX_PATH] = {0};
                LPCTSTR szLast = _tcsrchr(szPathTree,'\\');
                _tcsnccpy(szPrev,szPathTree,(int)(szLast-szPathTree));
                if(CreateDirectoryTree(szPrev,lpSecurityAttributes)){
                    bSuccess = CreateDirectory(szPathTree,lpSecurityAttributes)!=0;
                    if(!bSuccess){
                        bSuccess = (GetLastError()==ERROR_ALREADY_EXISTS);
                    }
                }else{
                    bSuccess = false;
                }
            }
            break;
        default:
            bSuccess = false;
            break;
    }

    return bSuccess;
}
Andy
fuente
Un pequeño mod: si la ruta contiene barras invertidas, esto no funciona. Aquí: `LPCTSTR szLast = _tcsrchr (szPathTree, '\\');` Solo necesita agregar esto: `` `if (nullptr == szLast) {szLast = _tcsrchr (szPathTree, '/'); } `` `
Den-Jason
1
Gracias por la info. ¿Qué pasa si se mezcla un camino? es decir: c:\this\is\a/mixed/path\of\slashesNormalmente, las barras diagonales de Windows son barras invertidas. Lo que debería suceder es que la persona que llama debe desinfectar la ruta y asegurarse de que todas las barras sean correctas antes de llamar a este método.
Andy
3

Sé que es una pregunta antigua, pero aparece en los primeros lugares de los resultados de búsqueda de Google y las respuestas proporcionadas aquí no están realmente en C ++ o son demasiado complicadas.

Tenga en cuenta que en mi ejemplo createDirTree () es muy simple porque todo el trabajo pesado (verificación de errores, validación de ruta) debe realizarse mediante createDir () de todos modos. También createDir () debería devolver verdadero si el directorio ya existe o todo no funcionará.

Así es como lo haría en C ++:

#include <iostream>
#include <string>

bool createDir(const std::string dir)
{
    std::cout << "Make sure dir is a valid path, it does not exist and create it: "
              << dir << std::endl;
    return true;
}

bool createDirTree(const std::string full_path)
{
    size_t pos = 0;
    bool ret_val = true;

    while(ret_val == true && pos != std::string::npos)
    {
        pos = full_path.find('/', pos + 1);
        ret_val = createDir(full_path.substr(0, pos));
    }

    return ret_val;
}

int main()
{
    createDirTree("/tmp/a/b/c");
    return 0;
}

Por supuesto, la función createDir () será específica del sistema y ya hay suficientes ejemplos en otras respuestas sobre cómo escribirla para Linux, así que decidí omitirla.

Tom
fuente
1

Si dir no existe, créelo:

boost::filesystem::create_directories(boost::filesystem::path(output_file).parent_path().string().c_str()); 
Franco
fuente
1

Se han descrito muchos enfoques aquí, pero la mayoría de ellos necesitan una codificación dura de su ruta en su código. Existe una solución fácil para ese problema, utilizando QDir y QFileInfo, dos clases de marco Qt. Dado que ya está en un entorno Linux, debería ser fácil de usar Qt.

QString qStringFileName("path/to/the/file/that/dont/exist.txt");
QDir dir = QFileInfo(qStringFileName).dir();
if(!dir.exists()) {
        dir.mkpath(dir.path());
}

Asegúrese de tener acceso de escritura a esa ruta.

Mohammad Rahimi
fuente
0
mkdir -p /dir/to/the/file

touch /dir/to/the/file/thefile.ending
Tim Cooper
fuente
la -popción es lo que estoy buscando. ¡Gracias!
pregunta
0

Aquí está la función recursiva de C / C ++ que se utiliza dirname()para recorrer el árbol de directorios de abajo hacia arriba. Se detendrá tan pronto como encuentre un antepasado existente.

#include <libgen.h>
#include <string.h>

int create_dir_tree_recursive(const char *path, const mode_t mode)
{
    if (strcmp(path, "/") == 0) // No need of checking if we are at root.
        return 0;

    // Check whether this dir exists or not.
    struct stat st;
    if (stat(path, &st) != 0 || !S_ISDIR(st.st_mode))
    {
        // Check and create parent dir tree first.
        char *path2 = strdup(path);
        char *parent_dir_path = dirname(path2);
        if (create_dir_tree_recursive(parent_dir_path, mode) == -1)
            return -1;

        // Create this dir.
        if (mkdir(path, mode) == -1)
            return -1;
    }

    return 0;
}
ravinsp
fuente
-2

Los demás te dieron la respuesta correcta, pero pensé en demostrar otra cosa interesante que puedes hacer:

mkdir -p /tmp/a/{b,c}/d

Creará las siguientes rutas:

/tmp/a/b/d
/tmp/a/c/d

Las llaves le permiten crear varios directorios a la vez en el mismo nivel de la jerarquía, mientras que la -popción significa "crear directorios principales según sea necesario".

rmeador
fuente
después de ver la respuesta de Paul, me doy cuenta de que yo (y muchas otras personas)
entendí
Si alguien puede simplemente actualizar esto cambiando al sistema ("mkdir -p / tmp / a / {b, c} / d"), porque las preguntas no se tratan de hacerlo en shell ... sino a través de C ++.
Lipis
Creo que {a, b} funcionará tanto en shells derivados de sh como en shells derivados de csh. Sin embargo, no estoy seguro de si funcionará en un comando system ().
Paul Tomblin
1
@Lipis: hacer eso a través de system () no es una buena solución a la pregunta del OP. @Andy: Nunca lo había considerado antes, pero lo probé reemplazando "mkdir -p" con "echo" y se imprime "/ tmp / a / b / d / tmp / a / c / d", lo que sugiere que es el caparazón quien lo hace, no mkdir.
rmeador
@rmeador: si no es una buena solución, ¿tienes algo más que sugerir? Quiero hacer eso a través de C ++ ... ese es mi problema, no cómo hacerlo a través de shell ..
Lipis