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

109

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

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

/tmp/a/b/c

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

Lipis
источник
C ++ не имеет встроенных средств для создания каталогов и деревьев как таковых . Вам придется использовать C и системные вызовы или внешнюю библиотеку, такую ​​как Boost. C и системные вызовы будут зависеть от платформы.
jww
6
@noloader Большое спасибо, чувак ... но я думаю, что через 4 года я в значительной степени получил свой ответ, как вы можете видеть ниже 13 различными способами ...
Липис
Да, я был удивлен, что никто прямо не заявил, что вы не можете сделать это на C ++ (при условии, что вам нужен переносимый метод на C ++, работающий в Linux). Но вы, наверное, знали об этом;). Однако было много хороших предложений для непереносимого кода C.
jww
Что такое «C ++ / Linux»?
Гонки легкости на орбите,
3
@LightnessRacesinOrbit Это мои университетские годы в C ++ на Linux :)
Липис

Ответы:

59

В 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 (исправленная версия ответа) была проверкой для EEXISTin 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 (Stack Overflow Questions) на GitHub в виде файлов mkpath.cи mkpath.h(и т.д.) в подкаталоге src / so-0067-5039 .

Джонатан Леффлер
источник
2
Это наверняка быстрее системы. Система имеет много накладных расходов. По сути, процесс должен быть разветвлен, затем должны быть загружены как минимум два двоичных файла (один, вероятно, уже будет в кеше), один из которых будет еще одним форком другого, ...
ypnos
1
Я забыл: и тогда "mkdir -p" будет делать, по крайней мере, то же самое, что и указанный выше код!
ypnos
7
В этом коде есть тонкое состояние гонки, которое я действительно затронул. Это происходит только тогда, когда несколько программ запускаются одновременно и используют один и тот же путь к папке. Исправление - добавить, if (errno != EEXIST) { status = -1; }когда mkdir не работает.
Switch
2
@Switch: Спасибо. Это проблема использования stat()раньше mkdir(); это проблема TOCTOU (время проверки, время использования). Я попытался устранить ошибку с помощью сценария оболочки, запускающего 13 процессов в фоновом режиме, создавая тот же путь из 29 элементов, и мне не удалось его обработать. Затем я взломал тестовую программу на форк 20 раз и попросил каждого ребенка попробовать, и это помогло устранить ошибку. Фиксированный код будет иметь if (mkdir(path, mode) != 0 && errno != EEXIST) status = -1;. Это не показывает ошибку.
Джонатан Леффлер
2
@DavidMerinos: это заголовки ( jlss.h, emalloc.h), а не библиотеки. Тем не менее, код доступен в моем SOQ (переполнению стека Вопросы) репозиторий на GitHub в виде файлов jlss.h, emalloc.cи emalloc.hв SRC / libsoq подкаталоге. Вам нужно posixver.hтакже, и несколько других ( debug.h, stderr.c, stderr.h- я думаю , что это, но то , что вам нужно все должны быть в этом каталоге).
Джонатан Леффлер
157

Легко с Boost.Filesystem: create_directories

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

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

Benoît
источник
9
Что ж, большинство буст-библиотек предназначены только для заголовков, что означает отсутствие дополнительных затрат, кроме того, что вы используете. В случае с Boost.Filesystem он требует компиляции. На моем диске скомпилированная библиотека весит ~ 60КБ.
Benoît
1
@Lipis: уточните, какая у вас встроенная система. Я считаю, что он должен быть доступен практически в каждом дистрибутиве Linux.
Benoît
4
Что касается компиляторов C ++ 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.
Chunliang Lyu
5
boost :: filesystem - это не только заголовок: stackoverflow.com/questions/13604090/…
ftvs 01
2
ИМХО: В любом моем проекте, который призван сделать что-то значимое и выдержать испытание временем, ХОРОШО стоит компиляция иметь такой набор невероятно полезных и мощных стандартизированных инструментов, как boost. Многие из них уже вошли в стандартный C ++, и, конечно же, в будущем. Попробуйте, придерживайтесь этого, вы выиграете, если у вас будут более чем тривиальные потребности, и вы не хотите изобретать велосипед. :-)
moodboom
42
system("mkdir -p /tmp/a/b/c")

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

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

ChristopheD
источник
1
ЕСЛИ вы собираетесь дать решение в виде команды оболочки, было бы хорошо упомянуть систему (3)
dmckee --- котенок экс-модератора
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);

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

Пол Томблин
источник
Интересный факт: по крайней мере, в Solaris и HP / UX есть mkdirp (), хотя это явно не оптимально для переносимости.
Мартин Карпентер,
в том-то и дело .. что я не хочу вызывать все эти функции по отдельности.
Lipis
Вызов mkdir несколько раз будет намного быстрее, чем вызов системы один раз.
Пол Томблин,
Я не понимаю, что вы предлагаете: вызвать mkdir 4 раза с разными аргументами? ("/tmp/",...), ("/tmp/a/",...), ("/tmp/a/b/",...),("/tmp/a/b/c/",...)
Антонио
1
Опять же, довольно просто сделать один и тот же вызов три раза. Дело в том, чтобы дать людям достаточно информации, чтобы они могли писать код, а не писать код за них.
Пол Томблин
25

Вот мой пример кода (работает как для 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
Максим Суслов
источник
Я поддерживаю этот комментарий! Это (на удивление) нетривиальная задача найти переносимый C ++ способ создания каталога. Этот ответ требует больше голосов.
Мануэль Лафонд
1
В Windows isDirExist не работает, если конечный символ - обратная косая черта. Всегда возвращает false. Мне нужно изменить код на: std :: string dirPath (path); while ('\\' == * dirPath.rbegin ()) dirPath.pop_back (); ... а затем, конечно, передайте dirPath.c_str () в вызове _stat.
MiloDC
Windows API имеет «имена Non-ANSI для совместимости» для stat(связанные с __STDC__) нет необходимости в тесте прекомпилятора.
Сандберг
18

Следует отметить, что начиная с 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. Последнее потенциально не понадобится в будущем.

mcsim
источник
Также должен работать с достаточно новым Visual C ++ и "/ std: c ++ latest". См .: blogs.msdn.microsoft.com/vcblog/2018/05/07/… и developercommunity.visualstudio.com/content/problem/296680/…
Рон Бурк
9

Это похоже на предыдущее, но работает вперед по строке, а не рекурсивно назад. Оставляет в 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';

}
phorgan1
источник
7

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

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

Джейсон Коэн
источник
Ну, система ("mkdir ...") должна работать с Linux. Однако это не кроссплатформенность.
ChristopheD
Я второй, что говорит @MartinCarpenter
Джошуа Хеджес
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;
}
отметка
источник
для меня это лучшее решение! Спасибо!)))
neo
может это вопрос дампа, но какой префикс "::" перед mkdir?
Rayee Roded
1
Нашел ответ: :: гарантирует, что разрешение происходит из глобального пространства имен stackoverflow.com/questions/4269034/…
Rayee Roded
4

Итак, мне нужно 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;
}

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

jonasfj
источник
Добавлено в суть, так что я не забываю, где я положу его в следующий раз, когда он понадобится :) gist.github.com/jonasfj/7797272
jonasfj
2
Плохо пытаться изменить строку, которая передается как константа. Помимо прочего, это может привести к серьезным сбоям, если ему когда-либо будет передан строковый литерал.
Джонатан Леффлер,
2
Совершенно верно .... это на самом деле плохо ... вероятно, strcpy сделает это лучше ...
jonasfj
3

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

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

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

Вот как я бы сделал это на 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;
}

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

Том
источник
1

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

boost::filesystem::create_directories(boost::filesystem::path(output_file).parent_path().string().c_str()); 
Фрэнк
источник
1

Здесь было описано так много подходов, но большинство из них требует жесткого кодирования вашего пути в ваш код. Есть простое решение этой проблемы, используя 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());
}

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

Мохаммад Рахими
источник
0
mkdir -p /dir/to/the/file

touch /dir/to/the/file/thefile.ending
Тим Купер
источник
-pвариант это то , что я ищу. Спасибо!
asgs
0

Вот рекурсивная функция 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;
}
равинсп
источник
-2

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

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

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

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

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

rmeador
источник
увидев ответ Пола, я понимаю, что я (и многие другие) неправильно понял вопрос ...
rmeador
Если кто-то может просто обновить это, перейдя в систему ("mkdir -p / tmp / a / {b, c} / d"), потому что вопрос не в том, чтобы делать это в оболочке .. а через C ++.
Lipis
Я думаю, что {a, b} будет работать как в оболочках, производных от sh, так и от csh. Однако я не уверен, будет ли он работать в команде system ().
Пол Томблин,
1
@Lipis: выполнение этого через system () не является хорошим решением вопроса OP. @Andy: Я никогда не думал об этом раньше, но я просто протестировал это, заменив "mkdir -p" на "echo", и он распечатает "/ tmp / a / b / d / tmp / a / c / d", что предполагает, что это делает оболочка, а не mkdir.
rmeador
@rmeador: если это не лучшее решение, у вас есть что предложить? Я хочу сделать это через C ++ ... моя проблема не в том, как это сделать через оболочку ...
Липис