GNU/Linux >> Tutoriels Linux >  >> Linux

Comment puis-je créer une arborescence de répertoires en C++/Linux ?

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

est le chemin le plus court auquel je puisse penser (en termes de longueur de code, pas nécessairement de temps d'exécution).

Ce n'est pas multiplateforme mais fonctionnera sous Linux.


Facile avec Boost.Filesystem :create_directories

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

Renvoie :true si un nouveau répertoire a été créé, sinon false .


Voici mon exemple de code (il fonctionne à la fois pour Windows et 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;
}

Utilisation :

$ makePath 1/2 folderA/folderB/folderC
creating 1/2 ... OK
creating folderA/folderB/folderC ... OK

Avec C++17 ou version ultérieure, il existe l'en-tête standard <filesystem> avecfonctionstd::filesystem::create_directories qui devrait être utilisé dans les programmes C++ modernes. Cependant, les fonctions standard C++ n'ont pas l'argument explicitepermissions (mode) spécifique à POSIX.

Cependant, voici une fonction C qui peut être compilée avec des compilateurs 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 */

Les macros STRDUP() et FREE() sont des versions de vérification des erreurs de strdup() et free() , déclaré en emalloc.h (et implémenté en emalloc.c et estrdup.c ).Le "sysstat.h" l'en-tête traite des versions cassées de <sys/stat.h> et peut être remplacé par <sys/stat.h> sur les systèmes Unix modernes (mais il y avait beaucoup de problèmes en 1990). Et "mkpath.h" déclare mkpath() .

Le changement entre la v1.12 (version originale de la réponse) et la v1.13 (version modifiée de la réponse) était le test pour EEXIST endo_mkdir() .Ceci a été signalé comme nécessaire par Switch — merci, Switch. révision (mais les tests ne peuvent montrer que la présence de bogues, jamais leur absence). Le code affiché est maintenant la v1.16 ; des modifications esthétiques ou administratives ont été apportées depuis la v1.13 (telles que l'utilisation de mkpath.h au lieu de jlss.h etinclure <unistd.h> inconditionnellement dans le code de test uniquement). Il est raisonnable d'affirmer que "sysstat.h" doit être remplacé par <sys/stat.h> sauf si vous avez un système inhabituellement récalcitrant.

(Vous êtes par la présente autorisé(e) à utiliser ce code à toute fin avec attribution.)

Ce code est disponible dans mon référentiel SOQ (Stack Overflow Questions) sur GitHub sous forme de fichiers mkpath.c etmkpath.h (etc.) dans le sous-répertoire src/so-0067-5039.


Linux
  1. Comment créer un répertoire partagé pour tous les utilisateurs sous Linux

  2. Comment ajouter un répertoire à PATH sous Linux [avec exemples]

  3. Comment trouver le fichier le plus ancien dans une arborescence de répertoires sous Linux

  4. Comment créer un nouveau répertoire sous Linux

  5. Comment pouvons-nous créer plusieurs interfaces factices sous Linux ?

Comment afficher l'arborescence des répertoires sous Linux

Comment ajouter un répertoire à PATH sous Linux [Astuce rapide]

Comment copier un répertoire sous Linux

Comment créer/ajouter des utilisateurs sous Linux

Linux :ajouter un répertoire à PATH

Comment puis-je profiler du code C++ exécuté sous Linux ?