128 votes

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

Je veux un moyen facile de créer des répertoires multiples en C++/Linux.

Par exemple, je veux enregistrer un fichier lola.file dans le répertoire :

/tmp/a/b/c

mais si les répertoires ne sont pas là, je veux qu'ils soient créés automatiquement. Un exemple fonctionnel serait parfait.

0 votes

Le C++ ne dispose d'aucune fonction intégrée pour la création de répertoires et d'arbres. en soi . Vous devrez utiliser le C et les appels système ou une bibliothèque externe comme Boost. Le C et les appels système dépendent de la plate-forme.

7 votes

@noloader Merci beaucoup... mais je pense qu'après 4 ans, j'ai trouvé ma réponse comme vous pouvez le voir ci-dessous de 13 façons différentes...

0 votes

Oui, j'ai été surpris que personne n'ait explicitement indiqué que vous ne pouviez pas le faire en C++ (en supposant que vous vouliez une méthode portable en C++ qui fonctionne sous Linux). Mais vous le saviez probablement ;). Il y avait beaucoup de bonnes suggestions pour le code C non portable, cependant.

166voto

Benoît Points 10901

Facile avec Boost.Filesystem : create_directories

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

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

0 votes

C'est plutôt chouette. Quelle est l'importance de l'inclusion de boost dans un projet C++ ?

0 votes

C'est chouette mais ça ne marchera pas dans mon projet c'est un système linux embarqué et je suis un noob donc :)

9 votes

Eh bien, la plupart des bibliothèques Boost ne sont que des en-têtes, ce qui signifie qu'il n'y a pas de surcharge en dehors de ce que vous utilisez. Dans le cas de Boost.Filesystem, il faut cependant compiler. Sur mon disque, la bibliothèque compilée pèse ~60KB.

82voto

Jonathan Leffler Points 299946

Avec C++17 ou plus, il y a l'en-tête standard. <filesystem> avec fonction std::filesystem::create_directories qui devraient être utilisés dans les programmes C++ modernes. Les fonctions standard C++ n'ont pas les autorisations explicites spécifiques à POSIX. (mode) spécifique à POSIX.

Cependant, voici une fonction C qui peut être compilée avec les 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() y FREE() sont des versions de contrôle d'erreurs de strdup() y free() déclaré dans emalloc.h (et mis en œuvre dans emalloc.c y estrdup.c ). Le site "sysstat.h" L'en-tête traite des versions brisées de <sys/stat.h> et peut être remplacé par <sys/stat.h> sur les systèmes Unix modernes (mais il y a de nombreux 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 amendée de la réponse) était le test de EEXIST sur do_mkdir() . Cette mesure a été jugée nécessaire par Interrupteur - remercier vous, Switch. Le code de test a été mis à jour et a reproduit le problème sur un MacBook Pro (2.3GHz Intel Core i7, exécutant Mac OS X 10.7.4), et suggère que que le problème est corrigé dans la révision (mais les tests ne peuvent montrer que la présence de bogues, jamais leur absence). Le code présenté est maintenant la v1.16 ; il y a eu des changements cosmétiques ou administratifs. administratifs ont été effectués depuis la v1.13 (comme l'utilisation de mkpath.h au lieu de jlss.h et inclure <unistd.h> sans condition dans le code de test uniquement). Il est raisonnable d'argumenter que "sysstat.h" doit être remplacé par <sys/stat.h> sauf si vous avez un système exceptionnellement récalcitrant.

(Vous êtes par la présente autorisé à utiliser ce code à n'importe quelle fin avec attribution).

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

0 votes

Ok... le résultat de celui-ci est exactement ce que je voulais... ! Quelqu'un peut-il me dire si c'est plus rapide que system("mkdir -p /tmp/a/b/c")... parce que c'est tellement plus facile :)

2 votes

C'est sûrement plus rapide que le système. Le système implique beaucoup de frais généraux. Fondamentalement, le processus doit être bifurqué, puis au moins deux binaires doivent être chargés (l'un sera probablement déjà dans le cache), dont l'un sera encore un autre bifurcation de l'autre, ...

1 votes

J'oubliais : Et puis "mkdir -p" fera au moins la même chose que le code posté ci-dessus !

44voto

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

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

Il n'est pas multiplateforme mais fonctionne sous Linux.

1 votes

SI vous donnez la solution sous forme de commande shell, il serait bon de mentionner le système (3).

1 votes

N'oubliez pas que vous devrez vous échapper du chemin correctement s'il n'est pas contrôlé par vous. Sinon, vous créeriez un énorme trou de sécurité.

1 votes

Je me demande pourquoi les gens créent autant d'API dans WinAPI et Libc alors que vous pourriez créer des programmes entiers en utilisant le système. Sérieusement, pourquoi ce post a 42 upvotes, c'est un problème de sécurité majeur et une terrible habitude de programmation.

26voto

Paul Tomblin Points 83687
#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 aquí . Vous devrez peut-être faire des mkdirs séparés pour /tmp, /tmp/a, /tmp/a/b/ et ensuite /tmp/a/b/c car il n'y a pas d'équivalent de l'option -p dans l'API C. Soyez sûr d'ignorer l'errno EEXISTS pendant que vous faites les niveaux supérieurs.

0 votes

Fait amusant : au moins Solaris et HP/UX ont mkdirp(), bien qu'il ne soit clairement pas optimal pour la portabilité.

0 votes

C'est le point que je ne veux pas appeler toutes ces fonctions séparément.

0 votes

Appeler mkdir plusieurs fois sera beaucoup, beaucoup plus rapide que d'appeler system une fois.

8voto

Jason Cohen Points 36475

Tu as dit "C++" mais tout le monde ici semble penser "Bash shell".

Consultez le code source de gnu mkdir vous pourrez alors voir comment implémenter les commandes du shell en C++.

0 votes

Eh bien system("mkdir...") devrait faire l'affaire sous linux. Mais ce n'est pas une solution multiplateforme.

0 votes

Je suis d'accord avec ce que dit @MartinCarpenter.

0 votes

La question est confuse...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X