Wie kann ich einen Verzeichnisbaum in C ++ / Linux erstellen?

109

Ich möchte eine einfache Möglichkeit, mehrere Verzeichnisse in C ++ / Linux zu erstellen.

Zum Beispiel möchte ich eine Datei lola.file im Verzeichnis speichern:

/tmp/a/b/c

aber wenn die Verzeichnisse nicht da sind, möchte ich, dass sie automatisch erstellt werden. Ein funktionierendes Beispiel wäre perfekt.

Lipis
quelle
In C ++ sind keine Funktionen zum Erstellen von Verzeichnissen und Bäumen per se integriert . Sie müssen C- und Systemaufrufe oder eine externe Bibliothek wie Boost verwenden. C- und Systemaufrufe sind plattformabhängig.
Jww
6
@noloader Vielen Dank Mann .. aber ich denke, nach 4 Jahren habe ich so ziemlich meine Antwort bekommen, wie Sie unten auf 13 verschiedene Arten sehen können ...
Lipis
Ja, ich war überrascht, dass niemand ausdrücklich erklärt hat, dass Sie dies in C ++ nicht tun können (vorausgesetzt, Sie wollten eine portable Methode in C ++, die unter Linux funktioniert). Aber das wusstest du wahrscheinlich;). Es gab jedoch viele gute Vorschläge für nicht portablen C-Code.
Jww
Was ist "C ++ / Linux"?
Leichtigkeitsrennen im Orbit
3
@ LightnessRacesinOrbit Es ist meine Universitätsjahre in C ++ unter Linux :)
Lipis

Antworten:

59

Ab C ++ 17 gibt es den Standardheader <filesystem>mit Funktion std::filesystem::create_directories , der in modernen C ++ - Programmen verwendet werden sollte. Die C ++ - Standardfunktionen verfügen jedoch nicht über das POSIX-spezifische Argument für explizite Berechtigungen (Modus).

Hier ist jedoch eine C-Funktion, die mit C ++ - Compilern kompiliert werden kann.

/*
@(#)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 */

Die Makros STRDUP()und FREE()sind fehlerprüfende Versionen von strdup()und free(), deklariert in emalloc.h(und implementiert in emalloc.cund estrdup.c). Der "sysstat.h"Header befasst sich mit defekten Versionen von <sys/stat.h> und kann durch <sys/stat.h>moderne Unix-Systeme ersetzt werden (aber es gab bereits 1990 viele Probleme). Und "mkpath.h"erklärtmkpath() .

Der Wechsel zwischen v1.12 (Originalversion der Antwort) und v1.13 (geänderte Version der Antwort) war der Test für EEXISTin do_mkdir(). Dies wurde von Switch als notwendig herausgestellt - danke, Switch. Der Testcode wurde aktualisiert und das Problem auf einem MacBook Pro (2,3 GHz Intel Core i7 unter Mac OS X 10.7.4) reproduziert. Er weist darauf hin, dass das Problem in der Revision behoben wurde (Tests können jedoch nur das Vorhandensein von Fehlern anzeigen niemals ihre Abwesenheit). Der angezeigte Code ist jetzt v1.16; Seit Version 1.13 wurden kosmetische oder administrative Änderungen vorgenommen (z. B. Verwendung mkpath.hanstelle von jlss.hund <unistd.h>bedingungslos nur im Testcode enthalten ). Es ist vernünftig zu argumentieren, dass "sysstat.h"dies durch ersetzt werden sollte, es <sys/stat.h>sei denn, Sie haben ein ungewöhnlich widerspenstiges System.

(Sie erhalten hiermit die Erlaubnis, diesen Code für jeden Zweck mit Namensnennung zu verwenden.)

Dieser Code ist in meinem SOQ- Repository (Stack Overflow Questions) auf GitHub als Dateien mkpath.cund mkpath.h(usw.) im Unterverzeichnis src / so-0067-5039 verfügbar .

Jonathan Leffler
quelle
2
Es ist sicherlich schneller als das System. Das System ist mit viel Aufwand verbunden. Grundsätzlich muss der Prozess gegabelt werden, dann müssen mindestens zwei Binärdateien geladen werden (eine wird wahrscheinlich bereits im Cache sein), von denen eine weitere Gabelung der anderen sein wird, ...
ypnos
1
Ich habe vergessen: Und dann macht "mkdir -p" mindestens das Gleiche wie der oben angegebene Code!
Ypnos
7
In diesem Code gibt es eine subtile Rennbedingung, die ich tatsächlich getroffen habe. Dies geschieht nur, wenn mehrere Programme gleichzeitig gestartet werden und denselben Ordnerpfad erstellen. Das if (errno != EEXIST) { status = -1; }Update muss hinzugefügt werden, wenn mkdir fehlschlägt.
Wechseln Sie
2
@ Switch: Danke. Das ist das Problem bei der Verwendung stat()vor mkdir(); Es handelt sich um ein TOCTOU-Problem (Prüfzeitpunkt, Verwendungszeitpunkt). Ich habe versucht, den Fehler mit einem Shell-Skript zu kitzeln, das 13 Prozesse im Hintergrund ausführt und denselben Pfad mit 29 Elementen erstellt, und es nicht geschafft, ihn zu treffen. Dann habe ich das Testprogramm 20 Mal gehackt, um es zu gabeln und jedes Kind versuchen zu lassen, und das hat es geschafft, den Fehler zu treffen. Der feste Code wird haben if (mkdir(path, mode) != 0 && errno != EEXIST) status = -1;. Das zeigt den Fehler nicht.
Jonathan Leffler
2
@ DavidMerinos: Sie sind Header ( jlss.h, emalloc.h), keine Bibliotheken. Allerdings ist der Code in meinen verfügbar SOQ (Stack Overflow Questions) Repository auf GitHub als Dateien jlss.h, emalloc.cund emalloc.him src / libsoq Unterverzeichnis. Sie müssen posixver.hauch, und ein paar andere ( debug.h, stderr.c, stderr.h- ich glaube , das ist es, aber was Sie brauchen , um alle in diesem Verzeichnis sein sollte).
Jonathan Leffler
157

Einfach mit Boost.Filesystem: create_directories

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

Rückgabe: trueWenn andernfalls ein neues Verzeichnis erstellt wurde false.

Benoît
quelle
9
Nun, die meisten Boost-Bibliotheken sind nur Header, was bedeutet, dass außer dem, was Sie verwenden, kein Overhead entsteht. Im Fall von Boost.Filesystem muss es jedoch kompiliert werden. Auf meiner Festplatte wiegt die kompilierte Bibliothek ~ 60 KB.
Benoît
1
@ Lipis: Bitte genau angeben, was Ihr eingebettetes System ist. Ich glaube, es sollte auf so ziemlich jeder Linux-Distribution verfügbar sein.
Benoît
4
In Bezug auf die von @danijar erwähnten C ++ 11-Compiler machte der Kommentar hier klarer: 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 ist nicht nur Header: stackoverflow.com/questions/13604090/…
ftvs
2
IMHO: In jedem meiner Projekte, das etwas Bedeutendes tun und den Test der Zeit bestehen soll, lohnt es sich, eine Reihe unglaublich nützlicher und leistungsfähiger standardisierter Tools als Boost zu haben. Eine Tonne davon hat bereits den Weg in Standard-C ++ gefunden, sicherlich mit weiteren. Probieren Sie es aus, bleiben Sie dabei, Sie werden davon profitieren, wenn Sie mehr als nur triviale Bedürfnisse haben und das Rad nicht neu erfinden möchten. :-)
Moodboom
42
system("mkdir -p /tmp/a/b/c")

ist der kürzeste Weg, den ich mir vorstellen kann (in Bezug auf die Länge des Codes, nicht unbedingt die Ausführungszeit).

Es ist nicht plattformübergreifend, funktioniert aber unter Linux.

ChristopheD
quelle
1
WENN Sie die Lösung als Shell-Befehl geben, ist es gut, System (3) zu erwähnen
dmckee --- Ex-Moderator Kätzchen
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);

Von hier aus . Möglicherweise müssen Sie separate mkdirs für / tmp, / tmp / a, / tmp / a / b / und dann / tmp / a / b / c ausführen, da es in der C-API kein Äquivalent zum Flag -p gibt. Seien Sie sicher und ignorieren Sie die EEXISTS-Fehler, während Sie die oberen Ebenen ausführen.

Paul Tomblin
quelle
Unterhaltsame Tatsache: Zumindest Solaris und HP / UX haben mkdirp (), obwohl es für die Portabilität eindeutig nicht optimal ist.
Martin Carpenter
Das ist der Punkt ... dass ich nicht alle diese Funktionen separat aufrufen möchte.
Lipis
Ein paar Mal mkdir aufzurufen ist viel schneller als ein einmaliger Aufruf des Systems.
Paul Tomblin
Ich verstehe nicht, was Sie vorschlagen: mkdir 4 Mal mit verschiedenen Argumenten aufrufen? ("/tmp/",...), ("/tmp/a/",...), ("/tmp/a/b/",...),("/tmp/a/b/c/",...)
Antonio
1
Auch hier ist es ziemlich trivial, dreimal denselben Anruf zu tätigen. Es geht darum, den Menschen genügend Informationen zu geben, damit sie den Code schreiben können, und nicht, um den Code für sie zu schreiben.
Paul Tomblin
25

Hier ist mein Beispiel für Code (es funktioniert sowohl für Windows als auch für 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;
}

Verwendung:

$ makePath 1/2 folderA/folderB/folderC
creating 1/2 ... OK
creating folderA/folderB/folderC ... OK
Maxim Suslov
quelle
Ich stimme diesem Kommentar zu! Es ist (überraschenderweise) keine triviale Aufgabe, eine tragbare C ++ - Methode zum Erstellen eines Verzeichnisses zu finden. Diese Antwort braucht mehr Upvotes.
Manuel Lafond
1
Unter Windows funktioniert isDirExist nicht, wenn das nachfolgende Zeichen ein Backslash ist. Gibt immer false zurück. Ich muss den Code ändern in: std :: string dirPath (Pfad); while ('\\' == * dirPath.rbegin ()) dirPath.pop_back (); ... und übergeben Sie dann natürlich dirPath.c_str () im Aufruf an _stat.
MiloDC
Windows - API hat „Nicht-ANSI - Namen für Kompatibilität“ für stat(bezogen auf __STDC__) keine Notwendigkeit für den Precompiler - Test.
Sandburg
18

Es ist zu beachten, dass das Starten ab der C ++ 17-Dateisystemschnittstelle Teil der Standardbibliothek ist. Dies bedeutet, dass zum Erstellen von Verzeichnissen Folgendes möglich ist:

#include <filesystem>

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

Mehr Infos hier: https://en.cppreference.com/w/cpp/filesystem/create_directory

Zusätzlich muss man bei gcc "-std = c ++ 17" für CFLAGS verwenden. Und "-lstdc ++ fs" zu LDLIBS. Letzteres wird möglicherweise in Zukunft nicht mehr benötigt.

mcsim
quelle
Sollte auch mit Visual C ++ und "/ std: c ++ latest" funktionieren. Siehe: blogs.msdn.microsoft.com/vcblog/2018/05/07/… und Developercommunity.visualstudio.com/content/problem/296680/…
Ron Burk
9

Dies ähnelt dem vorherigen, funktioniert jedoch vorwärts durch die Zeichenfolge anstatt rekursiv rückwärts. Lässt errno mit dem richtigen Wert für den letzten Fehler. Wenn es einen führenden Schrägstrich gibt, gibt es eine zusätzliche Zeit durch die Schleife, die durch ein find_first_of () außerhalb der Schleife oder durch Erkennen des führenden / und der Einstellung vor 1 vermieden werden könnte. Die Effizienz ist gleich, unabhängig davon, ob wir von a eingerichtet werden erste Schleife oder ein Pre-Loop-Aufruf, und die Komplexität wäre (geringfügig) höher, wenn der Pre-Loop-Aufruf verwendet wird.

#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
quelle
7

Sie sagten "C ++", aber jeder hier scheint an "Bash Shell" zu denken.

Überprüfen Sie den Quellcode zu gnu mkdir; Dann können Sie sehen, wie Sie die Shell-Befehle in C ++ implementieren.

Jason Cohen
quelle
Nun, System ("mkdir ...") sollte den Trick unter Linux machen. Es ist jedoch nicht plattformübergreifend.
ChristopheD
Ich stimme dem zu, was @MartinCarpenter sagt
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;
}
Kennzeichen
quelle
Es ist die beste Lösung für mich! Vielen Dank!)))
Neo
Vielleicht ist es eine Dump-Frage, aber wie lautet das Präfix "::" vor mkdir?
Rayee Roded
1
Die Antwort gefunden, stellt :: sicher, dass die Auflösung über den globalen Namespace stackoverflow.com/questions/4269034/… erfolgt
Rayee Roded
4

Also, ich brauche mkdirp() heute und fand die Lösungen auf dieser Seite zu kompliziert. Daher habe ich einen ziemlich kurzen Ausschnitt geschrieben, der leicht für andere kopiert werden kann, die über diesen Thread stolpern. Ein Wunder, warum wir so viele Codezeilen benötigen.

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;
}

Wenn Sie das Casting von Konstanten und das vorübergehende Ändern der Zeichenfolge nicht mögen, führen Sie einfach a strdup()und free()danach aus.

jonasfj
quelle
Gepostet zu einem Kern auch, damit ich nicht vergesse, wo ich es das nächste Mal platziere, wenn ich es brauche :) gist.github.com/jonasfj/7797272
jonasfj
2
Es ist böse zu versuchen, eine Zeichenfolge zu ändern, die als Konstante übergeben wird. Abgesehen davon ist es wahrscheinlich, dass es zu dramatischen Fehlern kommt, wenn jemals ein String-Literal übergeben wird.
Jonathan Leffler
2
Völlig richtig ... das ist eigentlich schlecht ... wahrscheinlich würde strcpy das besser machen ...
jonasfj
3

Da dieser Beitrag in Google für "Create Directory Tree" einen hohen Rang einnimmt, werde ich eine Antwort veröffentlichen, die für Windows funktioniert - dies funktioniert mit der für UNICODE oder MBCS kompilierten Win32-API. Dies ist aus Marks Code oben portiert.

Da dies Windows ist, mit dem wir arbeiten, sind Verzeichnis-Trennzeichen BACK-Schrägstriche und keine Schrägstriche. Wenn Sie lieber Schrägstriche haben möchten, wechseln Sie '\\'zu'/'

Es wird funktionieren mit:

c:\foo\bar\hello\world

und

c:\foo\bar\hellp\world\

(dh: benötigt keinen abschließenden Schrägstrich, sodass Sie nicht danach suchen müssen.)

Beachten Sie, dass SHCreateDirectoryEx () veraltet ist und jederzeit aus zukünftigen Windows-Versionen entfernt werden kann, bevor Sie "Nur SHCreateDirectoryEx () in Windows verwenden" sagen .

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
quelle
Ein kleiner Mod - wenn der Pfad Backslashes enthält, funktioniert dies nicht. Hier: `LPCTSTR szLast = _tcsrchr (szPathTree, '\\');` Sie müssen nur Folgendes hinzufügen: `` `if (nullptr == szLast) {szLast = _tcsrchr (szPathTree, '/'); } `` `
Den-Jason
1
Danke für die Information. Was passiert, wenn ein Pfad gemischt wird? dh: In der c:\this\is\a/mixed/path\of\slashesRegel sind Windows-Schrägstriche Backslashes. Was passieren sollte, ist, dass der Aufrufer den Pfad bereinigt und sicherstellt, dass alle Schrägstriche korrekt sind, bevor er diese Methode aufruft.
Andy
3

Ich weiß, dass es eine alte Frage ist, aber sie taucht in den Google-Suchergebnissen häufig auf und die hier angegebenen Antworten sind nicht wirklich in C ++ oder etwas zu kompliziert.

Bitte beachten Sie, dass createDirTree () in meinem Beispiel sehr einfach ist, da das gesamte schwere Heben (Fehlerprüfung, Pfadüberprüfung) ohnehin von createDir () durchgeführt werden muss. Außerdem sollte createDir () true zurückgeben, wenn das Verzeichnis bereits vorhanden ist oder das Ganze nicht funktioniert.

So würde ich das in C ++ machen:

#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;
}

Natürlich wird die Funktion createDir () systemspezifisch sein und es gibt bereits genügend Beispiele in anderen Antworten, wie man sie für Linux schreibt, also habe ich beschlossen, sie zu überspringen.

Tom
quelle
1

Wenn dir nicht existiert, erstelle es:

boost::filesystem::create_directories(boost::filesystem::path(output_file).parent_path().string().c_str()); 
Frank
quelle
1

Es wurden hier so viele Ansätze beschrieben, aber die meisten von ihnen erfordern eine harte Codierung Ihres Pfades in Ihren Code. Für dieses Problem gibt es eine einfache Lösung: QDir und QFileInfo, zwei Klassen von Qt-Frameworks. Da Sie sich bereits in einer Linux-Umgebung befinden, sollte es einfach sein, Qt zu verwenden.

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

Stellen Sie sicher, dass Sie Schreibzugriff auf diesen Pfad haben.

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

touch /dir/to/the/file/thefile.ending
Tim Cooper
quelle
Die -pOption ist das, wonach ich suche. Vielen Dank!
Asgs
0

Hier ist die rekursive C / C ++ - Funktion, mit der dirname()der Verzeichnisbaum von unten nach oben durchlaufen wird. Es wird angehalten, sobald es einen vorhandenen Vorfahren findet.

#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
quelle
-2

Die anderen haben dir die richtige Antwort gegeben, aber ich dachte, ich würde eine andere nette Sache demonstrieren, die du tun kannst:

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

Erstellt die folgenden Pfade:

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

Mit den geschweiften Klammern können Sie mehrere Verzeichnisse gleichzeitig auf derselben Hierarchieebene -perstellen , während die Option "übergeordnete Verzeichnisse nach Bedarf erstellen" bedeutet.

rmeador
quelle
Nachdem ich die Antwort von Paulus gesehen habe, stelle ich fest, dass ich (und viele andere Leute) die Frage falsch verstanden habe ...
rmeador
Wenn jemand dies einfach aktualisieren kann, indem er zum System wechselt ("mkdir -p / tmp / a / {b, c} / d"), weil es bei den Fragen nicht darum geht, dies in der Shell zu tun, sondern über C ++.
Lipis
Ich denke, {a, b} wird sowohl in sh-abgeleiteten als auch in csh-abgeleiteten Shells funktionieren. Ich bin mir jedoch nicht sicher, ob es in einem system () -Befehl funktionieren wird.
Paul Tomblin
1
@Lipis: Dies über system () zu tun, ist keine gute Lösung für die Frage des OP. @Andy: Ich hatte das noch nie in Betracht gezogen, aber ich habe es nur getestet, indem ich "mkdir -p" durch "echo" ersetzt habe und es druckt "/ tmp / a / b / d / tmp / a / c / d" aus. was darauf hindeutet, dass es die Shell ist, die es tut, nicht mkdir.
Rmeador
@rmeador: Wenn es keine gute Lösung ist, haben Sie noch etwas zu empfehlen? Ich möchte das durch C ++ machen ... das ist mein Problem, nicht wie man das durch Shell macht ...
Lipis