GNU/Linux >> LINUX-Kenntnisse >  >> Linux

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

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.


Einfach mit Boost.Filesystem:create_directories

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

Rückgabe:true wenn ein neues Verzeichnis angelegt wurde, sonst false .


Hier ist mein Codebeispiel (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

Ab C++17 gibt es den Standard-Header <filesystem> mit Funktionstd::filesystem::create_directories die in modernen C++-Programmen verwendet werden sollte. Die C++-Standardfunktionen haben jedoch nicht das POSIX-spezifische Argument "explicitpermissions (mode)".

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 fehlerüberprüfende Versionen vonstrdup() und free() , deklariert in emalloc.h (und implementiert in emalloc.c und estrdup.c ).Die "sysstat.h" Header befasst sich mit fehlerhaften Versionen von <sys/stat.h> und kann durch <sys/stat.h> ersetzt werden auf modernen Unix-Systemen (aber 1990 gab es viele Probleme). Und "mkpath.h" erklärt mkpath() .

Der Wechsel zwischen v1.12 (ursprüngliche Version der Antwort) und v1.13 (geänderte Version der Antwort) war der Test für EEXIST indo_mkdir() Der Testcode wurde aktualisiert und reproduziert das Problem auf einem MacBookPro (2,3 GHz Intel Core i7 mit Mac OS X 10.7.4) und deutet darauf hin, dass das Problem behoben ist Überarbeitung (aber Tests können nur das Vorhandensein von Fehlern zeigen, niemals deren Abwesenheit). Der angezeigte Code ist jetzt v1.16; Seit v1.13 wurden kosmetische oder administrative Änderungen vorgenommen (z. B. mkpath.h verwenden statt jlss.h und fügen Sie <unistd.h> ein unbedingt nur im Testcode). Es ist vernünftig zu argumentieren, dass "sysstat.h" sollte durch <sys/stat.h> ersetzt werden es 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(Stack Overflow Questions)-Repository auf GitHub als Dateien mkpath.c verfügbar undmkpath.h (usw.) im Unterverzeichnis src/so-0067-5039.


Linux
  1. So erstellen Sie ein freigegebenes Verzeichnis für alle Benutzer in Linux

  2. So fügen Sie ein Verzeichnis zu PATH in Linux hinzu [mit Beispielen]

  3. So finden Sie die älteste Datei in einem Verzeichnisbaum in Linux

  4. So erstellen Sie ein neues Verzeichnis in Linux

  5. Wie erstelle ich ein temporäres Verzeichnis in C++?

So zeigen Sie die Verzeichnisbaumstruktur in Linux an

So fügen Sie ein Verzeichnis zu PATH in Linux hinzu [Kurztipp]

So kopieren Sie das Verzeichnis unter Linux

So erstellen/fügen Sie Benutzer in Linux hinzu

Linux:Fügen Sie PATH ein Verzeichnis hinzu

Wie kann ich C++-Code profilieren, der unter Linux ausgeführt wird?