GNU/Linux >> Tutoriales Linux >  >> Linux

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

system("mkdir -p /tmp/a/b/c")

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

No es multiplataforma pero funcionará bajo Linux.


Fácil con Boost.Filesystem:create_directories

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

Devuelve:true si se creó un nuevo directorio, de lo contrario false .


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

Con C++17 o posterior, existe el encabezado estándar <filesystem> con funciónstd::filesystem::create_directories que debería 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ífico 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() , declarado en emalloc.h (e implementado en emalloc.c y estrdup.c ).El "sysstat.h" el encabezado trata con versiones rotas de <sys/stat.h> y puede ser reemplazado por <sys/stat.h> en los 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 modificada de la respuesta) fue la prueba para EEXIST en do_mkdir() .Switch señaló que esto era necesario. Gracias, Switch. El código de prueba se actualizó y reprodujo el problema en una MacBookPro (2,3 GHz Intel Core i7, con Mac OS X 10.7.4) y sugiere que el problema se solucionó en el 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 estéticos o administrativos desde v1.13 (como usar mkpath.h en lugar de jlss.h e incluir <unistd.h> incondicionalmente solo en el código de prueba). Es razonable argumentar que "sysstat.h" debe ser reemplazado por <sys/stat.h> a 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.c y mkpath.h (etc.) en el subdirectorio thesrc/so-0067-5039.


Linux
  1. Cómo crear un directorio compartido para todos los usuarios en Linux

  2. Cómo agregar un directorio a PATH en Linux [con ejemplos]

  3. ¿Cómo crear un directorio temporal en C++?

  4. ¿Puedo agregar un acceso directo para reemplazar una ruta en Linux?

  5. ¿Cómo crear comandos personalizados en Unix/Linux?

Cómo ver la estructura del árbol de directorios en Linux

Cómo agregar un directorio a PATH en Linux [Consejo rápido]

Cómo copiar un directorio en Linux

Cómo crear/agregar usuarios en Linux

Linux:agregar un directorio a PATH

¿Cómo puedo perfilar el código C++ que se ejecuta en Linux?