Как я могу создать дерево каталогов в C++/Linux?

Мне нужен простой способ создать несколько каталогов в C++/Linux.

Например, я хочу сохранить файл lola.file в каталоге:

/tmp/a/b/c

но если каталогов нет, я хочу, чтобы они создавались автоматически. Рабочий пример был бы идеальным.


person Lipis    schedule 23.03.2009    source источник
comment
C++ не имеет встроенных средств для создания каталогов и деревьев как таковых. Вам придется использовать C и системные вызовы или внешнюю библиотеку, например Boost. C и системные вызовы будут зависеть от платформы.   -  person jww    schedule 20.01.2014
comment
@noloader Большое спасибо, чувак .. но я думаю, что через 4 года я в значительной степени получил свой ответ, как вы можете видеть ниже, 13 различными способами ...   -  person Lipis    schedule 20.01.2014
comment
Да, я был удивлен, что никто прямо не заявил, что вы не можете сделать это на C++ (при условии, что вам нужен переносимый метод на C++, который работает в Linux). Но вы, наверное, знали это ;). Однако было много хороших предложений для непереносимого кода C.   -  person jww    schedule 20.01.2014
comment
Что такое С++/Линукс?   -  person Lightness Races in Orbit    schedule 22.01.2015
comment
@LightnessRacesinOrbit Это мои университетские годы на C++ в Linux :)   -  person Lipis    schedule 23.01.2015


Ответы (18)


В C++17 или более поздней версии есть стандартный заголовок <filesystem> с функцией std::filesystem::create_directories, которая следует использовать в современных программах на C++. Однако стандартные функции C++ не имеют специфического для POSIX аргумента явных разрешений (режим).

Однако вот функция C, которую можно скомпилировать с помощью компиляторов 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 */

Макросы STRDUP() и FREE() являются версиями strdup() и free() для проверки ошибок, объявленными в emalloc.h (и реализованными в emalloc.c и estrdup.c). Заголовок "sysstat.h" относится к неработающим версиям <sys/stat.h> и может быть заменен на <sys/stat.h> в современных системах Unix (но в 1990 году было много проблем). И "mkpath.h" объявляет mkpath().

Изменение между v1.12 (исходная версия ответа) и v1.13 (исправленная версия ответа) было тестом для EEXIST в do_mkdir(). На это указал Switch, спасибо, Switch. Тестовый код был обновлен и воспроизвел проблему на MacBook Pro (Intel Core i7 2,3 ГГц, работает под управлением Mac OS X 10.7.4) и предполагает, что проблема исправлена ​​в ревизии (но тестирование может показать только наличие ошибок). , никогда их отсутствие). Показанный код теперь v1.16; после версии 1.13 были внесены косметические или административные изменения (например, использование mkpath.h вместо jlss.h и безусловное включение <unistd.h> только в тестовый код). Разумно утверждать, что "sysstat.h" следует заменить на <sys/stat.h>, если только у вас необычно непокорная система.

(Настоящим вам дается разрешение на использование этого кода в любых целях с указанием авторства.)

Этот код доступен в моем репозитории SOQ (вопросы о переполнении стека) на GitHub в виде файлов mkpath.c и mkpath.h (и т. ) в подразделе src/so-0067-5039 -каталог.

person Jonathan Leffler    schedule 23.03.2009
comment
Хорошо... результат именно тот, что я хотел..! Может кто-нибудь сказать мне, быстрее ли это, чем система (mkdir -p /tmp/a/b/c).. потому что это намного проще :) - person Lipis; 24.03.2009
comment
Это, конечно, быстрее, чем система. Система имеет много накладных расходов. По сути, процесс должен быть разветвлен, затем должны быть загружены как минимум два двоичных файла (один, вероятно, уже будет в кеше), один из которых будет еще одним ответвлением другого, ... - person ypnos; 24.03.2009
comment
Я забыл: И тогда mkdir -p будет делать как минимум то же самое, что и код, опубликованный выше! - person ypnos; 24.03.2009
comment
В этом коде есть тонкое состояние гонки, которое я действительно наткнулся. Это происходит только тогда, когда несколько программ запускаются одновременно и создают один и тот же путь к папке. Исправление заключается в добавлении if (errno != EEXIST) { status = -1; } при сбое mkdir. - person Switch; 15.07.2012
comment
@Switch: Спасибо. В этом проблема с использованием stat() перед mkdir(); это проблема TOCTOU (время проверки, время использования). Я попытался исправить ошибку с помощью сценария оболочки, запускающего 13 процессов в фоновом режиме, создавая один и тот же путь из 29 элементов, и мне не удалось это сделать. Затем я взломал тестовую программу так, чтобы она разветвлялась 20 раз, и каждый ребенок пытался это сделать, и мне удалось найти ошибку. Фиксированный код будет иметь if (mkdir(path, mode) != 0 && errno != EEXIST) status = -1;. Это не показывает ошибку. - person Jonathan Leffler; 15.07.2012
comment
Где я могу найти библиотеки jlss и emalloc? Мои ошибки компилятора для этих 2. - person David Merinos; 14.01.2019
comment
@DavidMerinos: это заголовки (jlss.h, emalloc.h), а не библиотеки. Однако код доступен в моем репозитории SOQ (вопросы о переполнении стека) на GitHub в виде файлов jlss.h, emalloc.c. и emalloc.h в подкаталоге src/libsoq. Вам также понадобится posixver.h и несколько других (debug.h, stderr.c, stderr.h — я думаю, что все, но все, что вам нужно, должно быть в этом каталоге). - person Jonathan Leffler; 14.01.2019

Легко с Boost.Filesystem: create_directories

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

Возвращает: true, если был создан новый каталог, иначе false.

person Benoît    schedule 23.03.2009
comment
Это круто. Насколько велика подслушанная информация, включая повышение в проекте C++? - person Paul Tomblin; 23.03.2009
comment
Это здорово ... но не будет работать в моем проекте ... это встроенная система Linux ... и я нуб, так что ... :) - person Lipis; 23.03.2009
comment
Что ж, большинство библиотек Boost содержат только заголовки, что означает, что помимо того, что вы используете, нет дополнительных затрат. Однако в случае с Boost.Filesystem требуется компиляция. На моем диске скомпилированная библиотека весит ~60КБ. - person Benoît; 23.03.2009
comment
@Lipis: пожалуйста, уточните, какая у вас встроенная система. Я считаю, что он должен быть доступен практически в каждом дистрибутиве Linux. - person Benoît; 23.03.2009
comment
@Benoit: Думаю, ты прав... Может быть, когда-нибудь и попробую! - person Lipis; 24.03.2009
comment
С каждым часом Boost мне нравится все больше и больше :) - person Martin Ueding; 05.05.2013
comment
Что касается компиляторов С++ 11, упомянутых @danijar, комментарий здесь прояснил ситуацию: 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. - person Chunliang Lyu; 04.09.2013
comment
@ChunliangLyu Приятно знать. - person danijar; 04.09.2013
comment
boost::filesystem не только для заголовков: stackoverflow.com/questions/13604090/ - person ftvs; 01.11.2013
comment
Эта опция требует, чтобы вы скомпилировали boost::filesystem, что не рекомендуется. - person SmallChess; 16.04.2015
comment
ИМХО: В любом моем проекте, который должен сделать что-то значимое и выдержать испытание временем, НУЖНО иметь в компиляции такой набор невероятно полезных и мощных стандартизированных инструментов, как boost. Тонна этого уже вошла в стандартный C++, и, конечно, это еще не все. Попробуйте, придерживайтесь этого, вы выиграете, если у вас есть более чем тривиальные потребности, и вы не хотите изобретать велосипед. :-) - person moodboom; 09.12.2016
comment
Предостережение: время компиляции определенно увеличивается. - person moodboom; 09.12.2016
comment
Теперь у нас есть stdc++17 fs::create_directories! - person tigertang; 10.07.2019

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

это самый короткий способ, который я могу придумать (с точки зрения длины кода, не обязательно времени выполнения).

Он не кроссплатформенный, но будет работать под Linux.

person ChristopheD    schedule 23.03.2009
comment
ЕСЛИ вы собираетесь дать решение в виде команды оболочки, было бы неплохо упомянуть system (3) - person dmckee --- ex-moderator kitten; 23.03.2009
comment
Не забывайте, что вам нужно будет правильно уйти с пути, если он не контролируется вами. В противном случае вы создадите огромную дыру в безопасности. - person Vlad; 07.12.2020

Вот мой пример кода (работает как для Windows, так и для 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;
}

Применение:

$ makePath 1/2 folderA/folderB/folderC
creating 1/2 ... OK
creating folderA/folderB/folderC ... OK
person Maxim Suslov    schedule 23.04.2015
comment
Я поддерживаю этот комментарий! Это (на удивление) нетривиальная задача найти переносимый способ C++ для создания каталога. Этот ответ требует большего количества голосов. - person Manuel Lafond; 23.05.2017
comment
В Windows isDirExist не работает, если завершающий символ представляет собой обратную косую черту. Всегда возвращает ложь. Мне нужно изменить код на: std::string dirPath(path); в то время как ('\\' == *dirPath.rbegin()) dirPath.pop_back(); ... и затем, конечно же, передать dirPath.c_str() в вызове _stat. - person MiloDC; 09.09.2017
comment
Windows API имеет имена, отличные от ANSI, для совместимости с stat (связанными с __STDC__), нет необходимости в тесте прекомпилятора. - person Sandburg; 09.08.2019

#include <sys/types.h>
#include <sys/stat.h>

int status;
...
status = mkdir("/tmp/a/b/c", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);

Из здесь. Возможно, вам придется создать отдельные mkdirs для /tmp, /tmp/a, /tmp/a/b/, а затем /tmp/a/b/c, потому что в C API нет эквивалента флага -p. Будьте уверены и игнорируйте EEXISTS errno, пока вы делаете ошибки верхнего уровня.

person Paul Tomblin    schedule 23.03.2009
comment
Забавный факт: по крайней мере, в Solaris и HP/UX есть функция mkdirp(), хотя она явно не оптимальна для переносимости. - person Martin Carpenter; 23.03.2009
comment
в том то и дело.. что я не хочу вызывать все эти функции по отдельности. - person Lipis; 23.03.2009
comment
Вызов mkdir несколько раз будет намного быстрее, чем вызов system один раз. - person Paul Tomblin; 23.03.2009
comment
Я не понимаю, что вы предлагаете: вызвать mkdir 4 раза с разными аргументами? ("/tmp/",...), ("/tmp/a/",...), ("/tmp/a/b/",...), ("/tmp/a/b/c/",...) - person Antonio; 29.04.2016
comment
@ Антонио, именно это я и предлагаю. 3 или 4 вызова mkdir намного эффективнее, чем однократный вызов system. Однако, если вам нужно создать каталог /tmp, у вас возникнут проблемы посерьезнее, чем я могу вам помочь. - person Paul Tomblin; 29.04.2016
comment
Как бы то ни было, это не дает общего ответа на вопрос. Например, в функции create_directories, принимающей на вход путь, потребуется предварительное разбиение входного пути. В этом решении достаточно объединить две строки для создания вызова системной команды. - person Antonio; 29.04.2016
comment
Разделение строки на каталоги является тривиальной обработкой строки. Я не стал показывать людям, как это сделать, потому что вопрос был не в этом. - person Paul Tomblin; 29.04.2016
comment
@PaulTomblin Вы даже не показали полный код, чтобы ответить на реальный вопрос. Полный код включает все вызовы mkdir, которые я упомянул в своем предыдущем комментарии. - person Antonio; 29.04.2016
comment
Опять же, довольно тривиально сделать один и тот же вызов три раза. Смысл в том, чтобы дать людям достаточно информации, чтобы они могли написать код, а не писать код за них. - person Paul Tomblin; 29.04.2016

Следует отметить, что начиная с C++17 интерфейс файловой системы является частью стандартной библиотеки. Это означает, что для создания каталогов можно использовать следующее:

#include <filesystem>

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

Подробнее здесь: https://en.cppreference.com/w/cpp/filesystem/create_directory

Кроме того, с gcc необходимо указать "-std=c++17" для CFLAGS. И "-lstdc++fs" для LDLIBS. Последнее потенциально не потребуется в будущем.

person mcsim    schedule 29.10.2018
comment
Также должен работать с достаточно новым Visual C++ и /std:c++latest. См.: блоги. .msdn.microsoft.com/vcblog/2018/05/07/ и developercommunity.visualstudio.com/content/problem/296680/ - person Ron Burk; 21.11.2018

Это похоже на предыдущее, но работает по строке вперед, а не рекурсивно назад. Оставляет errno с правильным значением для последней ошибки. Если есть косая черта в начале, в цикле есть дополнительное время, которого можно было бы избежать с помощью одного find_first_of() вне цикла или путем обнаружения начального / и установки pre в 1. Эффективность одинакова, независимо от того, настроены ли мы с помощью первый цикл или вызов перед циклом, и сложность будет (немного) выше при использовании вызова перед циклом.

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

}
person phorgan1    schedule 15.10.2012

Вы сказали «С++», но все здесь, похоже, думают о «оболочке Bash».

Ознакомьтесь с исходным кодом gnu mkdir; тогда вы сможете увидеть, как реализовать команды оболочки на C++.

person Jason Cohen    schedule 23.03.2009
comment
Ну, система (mkdir...) должна помочь в Linux. Хотя он не кроссплатформенный. - person ChristopheD; 23.03.2009
comment
Я согласен с тем, что говорит @MartinCarpenter - person Joshua Hedges; 18.12.2011

Итак, мне нужно mkdirp() сегодня, и я нашел решения на этой странице слишком сложными. Поэтому я написал довольно короткий фрагмент, который легко скопировать для других, кто наткнется на эту тему и удивится, зачем нам нужно так много строк кода.

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

Если вам не нравится приведение констант и временное изменение строки, просто выполните strdup() и free() после этого.

person jonasfj    schedule 19.09.2013
comment
Опубликовано в суть, так что я не забуду, куда я поместил это в следующий раз, когда это понадобится :) .github.com/jonasfj/7797272 - person jonasfj; 05.12.2013
comment
Зло пытаться изменить строку, которая передается как константа. Помимо всего прочего, это может привести к серьезным сбоям, если ему когда-либо будет передан строковый литерал. - person Jonathan Leffler; 22.06.2015
comment
Совершенно верно... это на самом деле плохо... возможно, strcpy сделал бы это лучше... - person jonasfj; 24.06.2015

Поскольку этот пост имеет высокий рейтинг в Google для «Создать дерево каталогов», я собираюсь опубликовать ответ, который будет работать для Windows — он будет работать с использованием Win32 API, скомпилированного для UNICODE или MBCS. Это перенесено из кода Марка выше.

Поскольку мы работаем с Windows, разделителями каталогов являются обратные, а не прямые косые черты. Если вы предпочитаете косую черту, измените '\\' на '/'.

Он будет работать с:

c:\foo\bar\hello\world

а также

c:\foo\bar\hellp\world\

(т.е. не требует косой черты в конце, поэтому вам не нужно ее проверять.)

Прежде чем сказать «Просто используйте SHCreateDirectoryEx() в Windows», обратите внимание, что SHCreateDirectoryEx() устарела и может быть удалена в любое время из будущих версий 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;
}
person Andy    schedule 11.12.2012
comment
Один небольшой мод - если путь содержит обратную косую черту, это не работает. Здесь: ` LPCTSTR szLast = _tcsrchr(szPathTree, '\\'); Вам просто нужно добавить это: ``` if (nullptr == szLast) { szLast = _tcsrchr(szPathTree, '/'); } ``` - person Den-Jason; 28.01.2020
comment
Спасибо за информацию. Что произойдет, если путь смешанный? то есть: c:\this\is\a/mixed/path\of\slashes Обычно косая черта Windows является обратной косой чертой. Что должно произойти, так это то, что вызывающая сторона должна очистить путь и убедиться, что все косые черты верны перед вызовом этого метода. - person Andy; 28.01.2020

Я знаю, что это старый вопрос, но он появляется высоко в результатах поиска Google, а ответы, представленные здесь, на самом деле не на C ++ или слишком сложны.

Обратите внимание, что в моем примере функция createDirTree() очень проста, потому что вся тяжелая работа (проверка ошибок, проверка пути) в любом случае должна выполняться функцией createDir(). Также createDir() должен возвращать true, если каталог уже существует или все это не будет работать.

Вот как бы я сделал это на С++:

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

Конечно, функция createDir() будет специфичной для системы, и в других ответах уже достаточно примеров, как написать ее для Linux, поэтому я решил ее пропустить.

person Tom    schedule 29.01.2014

Если каталог не существует, создайте его:

boost::filesystem::create_directories(boost::filesystem::path(output_file).parent_path().string().c_str()); 
person Frank    schedule 07.12.2016

Здесь было описано так много подходов, но большинство из них требуют жесткого кодирования вашего пути в вашем коде. Существует простое решение этой проблемы с использованием QDir и QFileInfo, двух классов инфраструктуры Qt. Поскольку вы уже работаете в среде Linux, вам будет легко использовать Qt.

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

Убедитесь, что у вас есть доступ для записи к этому пути.

person Mohammad Rahimi    schedule 25.01.2020

Вот рекурсивная функция C/C++, которая использует dirname() для обхода дерева каталогов снизу вверх. Он остановится, как только найдет существующего предка.

#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;
}
person ravinsp    schedule 18.04.2020


Другие дали вам правильный ответ, но я решил продемонстрировать еще одну изящную вещь, которую вы можете сделать:

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

Создаст следующие пути:

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

Скобки позволяют создавать несколько каталогов одновременно на одном уровне иерархии, тогда как параметр -p означает «создавать родительские каталоги по мере необходимости».

person rmeador    schedule 23.03.2009
comment
увидев ответ Пола, я понимаю, что я (и многие другие люди) неправильно поняли вопрос... - person rmeador; 23.03.2009
comment
Если кто-то может просто обновить это, изменив на system (mkdir -p /tmp/a/{b,c}/d), потому что вопросы не в том, чтобы сделать это в оболочке, а через C++. - person Lipis; 23.03.2009
comment
Я думаю, что {a,b} будет работать как в оболочках, производных от sh, так и в оболочках, производных от csh. Однако я не уверен, что это будет работать в команде system(). - person Paul Tomblin; 23.03.2009
comment
@Lipis: делать это через system() не является хорошим решением вопроса ОП. @Andy: я никогда не думал об этом раньше, но я только что проверил это, заменив mkdir -p на echo, и он распечатал /tmp/a/b/d /tmp/a/c/d, что предполагает, что это оболочка делать это, а не mkdir. - person rmeador; 23.03.2009
comment
@rmeador: если это не очень хорошее решение, можете ли вы предложить что-то еще? Я хочу сделать это через С++... это моя проблема, а не как это сделать через оболочку. - person Lipis; 23.03.2009
comment
@Lipis: здесь есть много других ответов, которые предлагают, как это сделать с помощью C++. Мне больше нравится Boost, поэтому мне нравится ответ Бенуа. Решение Пола также должно работать, если ваша встроенная система очень ограничена. Кстати, извините, что не заметил, что вы ОП в моем последнем комментарии :) - person rmeador; 24.03.2009
comment
Обратите внимание, что строгие оболочки POSIX (dash и т. д.) не поддерживают раскрытие фигурных скобок. - person Jonathan Leffler; 19.03.2016

person    schedule
comment
это лучшее решение для меня! Спасибо!))) - person neo; 06.12.2016
comment
может быть, это вопрос дампа, но что такое префикс :: перед mkdir? - person Rayee Roded; 16.02.2017
comment
Нашел ответ, :: гарантирует, что разрешение происходит из глобального пространства имен stackoverflow.com/questions/4269034/ - person Rayee Roded; 16.02.2017

person    schedule
comment
вариант -p - это то, что я ищу. Спасибо! - person asgs; 14.02.2012