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.