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

109

Je veux un moyen simple de créer plusieurs répertoires en C ++ / Linux.

Par exemple, je souhaite 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 de travail serait parfait.

Lipis
la source
C ++ ne dispose d'aucune fonctionnalité intégrée pour créer des répertoires et des arborescences en soi . Vous devrez utiliser C et les appels système ou une bibliothèque externe comme Boost. Les appels C et système dépendront de la plate-forme.
jww
6
@noloader Merci beaucoup mec .. mais je pense qu'après 4 ans, j'ai quasiment eu ma réponse comme vous pouvez le voir ci-dessous de 13 manières différentes ...
Lipis
Ouais, j'ai été surpris que personne n'ait explicitement déclaré que vous ne pouvez 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 cependant beaucoup de bonnes suggestions pour le code C non portable.
jww
Qu'est-ce que "C ++ / Linux"?
Courses de légèreté en orbite le
3
@LightnessRacesinOrbit C'est mes années universitaires en C ++ sur Linux :)
Lipis

Réponses:

59

Avec C ++ 17 ou version ultérieure, il y a l'en-tête standard <filesystem>avec une fonction std::filesystem::create_directories qui devrait être utilisée dans les programmes C ++ modernes. Les fonctions standard C ++ n'ont cependant pas l'argument d'autorisations explicites (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ées dans emalloc.h(et implémentées dans emalloc.cet estrdup.c). L'en- "sysstat.h"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 de nombreux problèmes en 1990). Et "mkpath.h"déclare mkpath().

Le changement entre v1.12 (version originale de la réponse) et v1.13 (version modifiée de la réponse) était le test pour EEXISTdans do_mkdir(). Cela a été signalé comme nécessaire par Switch - merci, Switch. Le code de test a été mis à jour et reproduit le problème sur un MacBook Pro (Intel Core i7 2,3 GHz, exécutant Mac OS X 10.7.4), et suggère que le problème est résolu dans la révision (mais les tests ne peuvent montrer que la présence de bogues , jamais leur absence). Le code affiché est maintenant v1.16; des modifications cosmétiques ou administratives ont été apportées depuis la v1.13 (comme l'utilisation à la mkpath.hplace de jlss.het l'inclusion <unistd.h>inconditionnelle dans le code de test uniquement). Il est raisonnable de dire que cela "sysstat.h"devrait être remplacé par à <sys/stat.h>moins que vous n'ayez un système inhabituellement récalcitrant.

(Vous êtes autorisé par la présente à 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.cet mkpath.h(etc.) dans le sous-répertoire src / so-0067-5039 .

Jonathan Leffler
la source
2
C'est sûrement plus rapide que le système. Le système a beaucoup de frais généraux impliqués. Fondamentalement, le processus doit être forké, puis au moins deux binaires doivent être chargés (l'un sera probablement déjà dans le cache), sur lequel sera encore un autre fork de l'autre, ...
ypnos
1
J'ai oublié: Et puis "mkdir -p" fera au moins la même chose que le code affiché ci-dessus!
ypnos
7
Il y a une condition de concurrence subtile dans ce code que je rencontre réellement. Cela ne se produit que lorsque plusieurs programmes démarrent simultanément et créent le même chemin de dossier. Le correctif consiste à ajouter if (errno != EEXIST) { status = -1; }lorsque mkdir échoue.
Changez le
2
@Switch: Merci. C'est le problème avec l'utilisation stat()avant mkdir(); il s'agit d'un problème TOCTOU (heure de contrôle, heure d'utilisation). J'ai essayé de chatouiller le bogue avec un script shell exécutant 13 processus en arrière-plan créant le même chemin de 29 éléments, et je n'ai pas réussi à le frapper. Ensuite, j'ai piraté le programme de test pour le fork 20 fois et j'ai demandé à chaque enfant d'essayer, et cela a réussi à toucher le bogue. Le code fixe aura if (mkdir(path, mode) != 0 && errno != EEXIST) status = -1;. Cela ne montre pas le bogue.
Jonathan Leffler
2
@DavidMerinos: ce sont des en-têtes ( jlss.h, emalloc.h), pas des bibliothèques. Cependant, le code est disponible dans mon référentiel SOQ (Stack Overflow Questions) sur GitHub sous forme de fichiers jlss.h, emalloc.cet emalloc.hdans le sous-répertoire src / libsoq . Vous aurez besoin posixver.haussi, et quelques autres ( debug.h, stderr.c, stderr.h- je pense que ce, mais ce que vous avez besoin devraient tous être dans ce répertoire).
Jonathan Leffler
157

Facile avec Boost.Filesystem: create_directories

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

Retourne: truesi un nouveau répertoire a été créé, sinon false.

Benoît
la source
9
Eh bien, la plupart des bibliothèques boost sont uniquement en-tête, ce qui signifie qu'il n'y a pas de surcharge en plus de ce que vous utilisez. Dans le cas de Boost.Filesystem, il nécessite cependant une compilation. Sur mon disque, la bibliothèque compilée pèse ~ 60 Ko.
Benoît
1
@Lipis: veuillez préciser ce qu'est votre système embarqué. Je pense qu'il devrait être disponible sur à peu près toutes les distributions Linux.
Benoît
4
En ce qui concerne les compilateurs C ++ 11 mentionnés par @danijar, le commentaire ici l' a clarifié: 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 n'est pas uniquement en-tête: stackoverflow.com/questions/13604090/…
ftvs
2
IMHO: Dans n'importe quel projet de la mienne qui est destiné à faire quelque chose d'important et à résister à l'épreuve du temps, il vaut vraiment la peine d'être compilé pour avoir un ensemble d'outils standardisés incroyablement utiles et puissants comme boost. Une tonne de celui-ci a déjà fait son chemin dans le C ++ standard, certainement avec d'autres à venir. Essayez-le, respectez-le, vous en bénéficierez si vous avez plus que des besoins insignifiants et que vous ne voulez pas réinventer la roue. :-)
moodboom
42
system("mkdir -p /tmp/a/b/c")

est le moyen 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.

ChristopheD
la source
1
SI vous allez donner la solution sous forme de commande shell, il serait bon de mentionner system (3)
dmckee --- ex-moderator chaton
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);

D' ici . 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'indicateur -p dans l'API C. Soyez sûr et ignorez l'erreur EEXISTS pendant que vous faites les niveaux supérieurs.

Paul Tomblin
la source
Fait amusant: au moins Solaris et HP / UX ont mkdirp (), bien que ce ne soit clairement pas optimal pour la portabilité.
Martin Carpenter
c'est le point .. que je ne veux pas appeler toutes ces fonctions séparément.
Lipis
Appeler mkdir plusieurs fois sera bien plus rapide que d'appeler le système une fois.
Paul Tomblin
Je ne comprends pas ce que vous suggérez: appeler mkdir 4 fois avec des arguments différents? ("/tmp/",...), ("/tmp/a/",...), ("/tmp/a/b/",...),("/tmp/a/b/c/",...)
Antonio
1
Encore une fois, il est assez trivial de faire le même appel trois fois. Le but est de donner aux gens suffisamment d'informations pour qu'ils puissent écrire le code, pas d'écrire le code pour eux.
Paul Tomblin
25

Voici mon exemple de code (cela 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;
}

Usage:

$ makePath 1/2 folderA/folderB/folderC
creating 1/2 ... OK
creating folderA/folderB/folderC ... OK
Maxim Suslov
la source
J'appuie ce commentaire! Ce n'est (étonnamment) pas une tâche facile de trouver un moyen portable C ++ de créer un répertoire. Cette réponse a besoin de plus de votes positifs.
Manuel Lafond
1
Sous Windows, isDirExist ne fonctionne pas si le caractère de fin est une barre oblique inverse. Renvoie toujours false. Je dois modifier le code en: std :: string dirPath (path); while ('\\' == * dirPath.rbegin ()) dirPath.pop_back (); ... puis, bien sûr, passez dirPath.c_str () dans l'appel à _stat.
MiloDC
L'API Windows a des «noms non-ANSI pour la compatibilité» pour stat(liés à __STDC__) aucun besoin de test de précompilateur.
Sandburg
18

Il est à noter qu'à partir de l'interface du système de fichiers C ++ 17 fait partie de la bibliothèque standard. Cela signifie que l'on peut avoir les éléments suivants pour créer des répertoires:

#include <filesystem>

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

Plus d'informations ici: https://en.cppreference.com/w/cpp/filesystem/create_directory

De plus, avec gcc, il faut "-std = c ++ 17" vers CFLAGS. Et "-lstdc ++ fs" vers LDLIBS. Ce dernier ne sera potentiellement pas nécessaire à l'avenir.

mcsim
la source
Devrait également fonctionner avec assez nouveau Visual C ++ et "/ std: c ++ latest". Voir: blogs.msdn.microsoft.com/vcblog/2018/05/07/… et developercommunity.visualstudio.com/content/problem/296680/…
Ron Burk
9

Ceci est similaire au précédent mais fonctionne en avant à travers la chaîne au lieu de recursivement en arrière. Laisse errno avec la bonne valeur pour le dernier échec. S'il y a une barre oblique au début, il y a un temps supplémentaire dans la boucle qui aurait pu être évité via un find_first_of () en dehors de la boucle ou en détectant le premier / et en mettant pre à 1. L'efficacité est la même que nous soyons configurés par un première boucle ou un appel de pré-boucle, et la complexité serait (légèrement) plus élevée lors de l'utilisation de l'appel de pré-boucle.

#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
la source
7

Vous avez dit «C ++», mais tout le monde ici semble penser «shell Bash».

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

Jason Cohen
la source
Le système ("mkdir ...") devrait faire l'affaire sous Linux. Ce n'est cependant pas multiplateforme.
ChristopheD
Je seconde ce que @MartinCarpenter dit
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;
}
marque
la source
c'est la meilleure solution pour moi! Merci!)))
neo
c'est peut-être une question de vidage, mais quel est le préfixe "::" avant mkdir?
Rayee Roded
1
Trouvé la réponse, le :: assure que la résolution se produit à partir de l'espace de noms global stackoverflow.com/questions/4269034/…
Rayee Roded
4

J'ai donc besoin mkdirp()aujourd'hui, et trouve les solutions sur cette page trop compliquées. Par conséquent, j'ai écrit un extrait assez court, qui peut facilement être copié pour ceux qui tombent sur ce fil de discussion et se demandent pourquoi nous avons besoin de tant de lignes de code.

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 vous n'aimez pas le cast const et la modification temporaire de la chaîne, faites simplement un strdup()et free()après.

Jonasfj
la source
Publié dans l'essentiel aussi, donc je n'oublie pas où je le mets la prochaine fois que j'en ai besoin :) gist.github.com/jonasfj/7797272
jonasfj
2
C'est mal d'essayer de modifier une chaîne qui est passée comme constante. En dehors de tout cela, il est susceptible de conduire à des échecs dramatiques s'il est jamais passé une chaîne littérale.
Jonathan Leffler
2
Parfaitement vrai .... c'est en fait mauvais ... probablement strcpy ferait mieux ...
jonasfj
3

Étant donné que cet article est bien classé dans Google pour "Créer une arborescence de répertoires", je vais publier une réponse qui fonctionnera pour Windows - cela fonctionnera en utilisant l'API Win32 compilée pour UNICODE ou MBCS. Ceci est porté à partir du code de Mark ci-dessus.

Puisqu'il s'agit de Windows avec lequel nous travaillons, les séparateurs de répertoire sont des barres obliques ARRIÈRE, pas des barres obliques. Si vous préférez avoir des barres obliques, passez '\\'à'/'

Cela fonctionnera avec:

c:\foo\bar\hello\world

et

c:\foo\bar\hellp\world\

(c'est-à-dire: n'a pas besoin de barre oblique de fin, vous n'avez donc pas à le vérifier.)

Avant de dire «Utilisez simplement SHCreateDirectoryEx () sous Windows», notez que SHCreateDirectoryEx () est obsolète et pourrait être supprimé à tout moment des futures versions 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
la source
Un petit mod - si le chemin contient des barres obliques inverses, cela ne fonctionne pas. Ici: `LPCTSTR szLast = _tcsrchr (szPathTree, '\\');` Il vous suffit d'ajouter ceci: `` `` if (nullptr == szLast) {szLast = _tcsrchr (szPathTree, '/'); } `` ``
Den-Jason
1
Merci pour l'info. Que se passe-t-il si un chemin est mixte? Par exemple: les c:\this\is\a/mixed/path\of\slashesbarres obliques Windows sont généralement des barres obliques inverses. Ce qui devrait se passer, c'est que l'appelant doit nettoyer le chemin et s'assurer que toutes les barres obliques sont correctes avant d'appeler cette méthode.
Andy le
3

Je sais que c'est une vieille question, mais elle apparaît en bonne place dans les résultats de recherche Google et les réponses fournies ici ne sont pas vraiment en C ++ ou sont un peu trop compliquées.

Veuillez noter que dans mon exemple, createDirTree () est très simple car tout le travail lourd (vérification des erreurs, validation de chemin) doit être fait de toute façon par createDir (). De plus, createDir () devrait retourner true si le répertoire existe déjà ou si tout ne fonctionnera pas.

Voici comment je ferais cela 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;
}

Bien sûr, la fonction createDir () sera spécifique au système et il y a déjà suffisamment d'exemples dans d'autres réponses pour l'écrire pour Linux, j'ai donc décidé de l'ignorer.

À M
la source
1

Si dir n'existe pas, créez-le:

boost::filesystem::create_directories(boost::filesystem::path(output_file).parent_path().string().c_str()); 
Franc
la source
1

De nombreuses approches ont été décrites ici, mais la plupart d'entre elles nécessitent un codage en dur de votre chemin dans votre code. Il existe une solution simple à ce problème, en utilisant QDir et QFileInfo, deux classes du framework Qt. Étant donné que vous êtes déjà dans un environnement Linux, il devrait être facile d'utiliser Qt.

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

Assurez-vous que vous disposez d'un accès en écriture à ce chemin.

Mohammad Rahimi
la source
0
mkdir -p /dir/to/the/file

touch /dir/to/the/file/thefile.ending
Tim Cooper
la source
l' -poption est ce que je recherche. Merci!
asgs
0

Voici la fonction récursive C / C ++ qui utilise dirname()pour parcourir de bas en haut l'arborescence de répertoires. Il s'arrêtera dès qu'il trouvera un ancêtre existant.

#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
la source
-2

Les autres vous ont donné la bonne réponse, mais j'ai pensé vous montrer une autre chose intéressante que vous pouvez faire:

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

Créera les chemins suivants:

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

Les accolades vous permettent de créer plusieurs répertoires à la fois au même niveau de la hiérarchie, alors que l' -poption signifie «créer des répertoires parents selon les besoins».

rmeador
la source
après avoir vu la réponse de Paul, je me rends compte que moi (et beaucoup d'autres personnes) j'ai mal compris la question ...
rmeador
Si quelqu'un peut simplement mettre à jour ceci en passant au système ("mkdir -p / tmp / a / {b, c} / d"), car les questions ne sont pas de le faire dans le shell ... mais via C ++.
Lipis
Je pense que {a, b} fonctionnera à la fois dans les shells dérivés de sh et de csh. Je ne sais pas si cela fonctionnera dans une commande system (), cependant.
Paul Tomblin
1
@Lipis: faire cela via system () n'est pas une bonne solution à la question du PO. @Andy: Je n'avais jamais envisagé cela auparavant, mais je l'ai juste testé en remplaçant "mkdir -p" par "echo" et il affiche "/ tmp / a / b / d / tmp / a / c / d", ce qui suggère que c'est le shell qui le fait, pas mkdir.
rmeador
@rmeador: si ce n'est pas une bonne solution, avez-vous autre chose à suggérer? Je veux faire cela via C ++ ... c'est mon problème, pas de savoir comment faire cela via shell ...
Lipis