Como posso criar árvore de diretório em C ++ / Linux?

109

Quero uma maneira fácil de criar vários diretórios em C ++ / Linux.

Por exemplo, quero salvar um arquivo lola.file no diretório:

/tmp/a/b/c

mas se os diretórios não estiverem lá, quero que sejam criados automaticamente. Um exemplo prático seria perfeito.

Lipis
fonte
C ++ não possui recursos internos para a criação de diretórios e árvores per se . Você terá que usar C e chamadas de sistema ou uma biblioteca externa como Boost. C e chamadas de sistema dependerão da plataforma.
jww
6
@noloader Muito obrigado cara .. mas acho que depois de 4 anos eu praticamente recebi minha resposta como você pode ver abaixo de 13 maneiras diferentes ...
Lipis
Sim, fiquei surpreso por ninguém ter afirmado explicitamente que você não pode fazer isso em C ++ (supondo que você queira um método portátil em C ++ que funcione no Linux). Mas tu provavelmente sabias disso ;). No entanto, houve muitas sugestões boas para código C não portátil.
jww
O que é "C ++ / Linux"?
Lightness Races in Orbit
3
@LightnessRacesinOrbit São meus anos de universidade em C ++ no Linux :)
Lipis

Respostas:

59

Com C ++ 17 ou posterior, existe o cabeçalho padrão <filesystem>com função std::filesystem::create_directories que deve ser usado em programas C ++ modernos. No entanto, as funções padrão C ++ não têm o argumento de permissões explícitas específicas de POSIX (modo).

No entanto, aqui está uma função C que pode ser compilada com compiladores 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 */

As macros STRDUP()e FREE()são versões de verificação de erros de strdup()e free(), declaradas em emalloc.h(e implementadas em emalloc.ce estrdup.c). O "sysstat.h"cabeçalho lida com versões quebradas <sys/stat.h> e pode ser substituído por <sys/stat.h>em sistemas Unix modernos (mas havia muitos problemas em 1990). E "mkpath.h"declaramkpath() .

A mudança entre v1.12 (versão original da resposta) e v1.13 (versão corrigida da resposta) foi o teste para EEXISTem do_mkdir(). Isso foi apontado como necessário por Switch - obrigado, Switch. O código de teste foi atualizado e reproduziu o problema em um MacBook Pro (2,3 GHz Intel Core i7, executando Mac OS X 10.7.4) e sugere que o problema foi corrigido na revisão (mas o teste só pode mostrar a presença de bugs , nunca sua ausência). O código mostrado agora é v1.16; houve mudanças cosméticas ou administrativas feitas desde v1.13 (como usar em mkpath.hvez de jlss.he incluir <unistd.h>incondicionalmente apenas no código de teste). É razoável argumentar que "sysstat.h"deve ser substituído por, a <sys/stat.h>menos que você tenha um sistema invulgarmente recalcitrante.

(Você recebe permissão para usar este código para qualquer finalidade com atribuição.)

Este código está disponível em meu repositório SOQ (Stack Overflow Questions) no GitHub como arquivos mkpath.ce mkpath.h(etc.) no subdiretório src / so-0067-5039 .

Jonathan Leffler
fonte
2
Certamente é mais rápido que o sistema. O sistema envolve muita sobrecarga. Basicamente, o processo tem que ser bifurcado, então pelo menos dois binários precisam ser carregados (um provavelmente já estará no cache), um dos quais será mais um bifurcação do outro, ...
ypnos
1
Esqueci: E então "mkdir -p" fará pelo menos o mesmo que o código postado acima!
ypnos
7
Há uma condição de corrida sutil neste código que eu realmente acertei. Isso só acontece quando vários programas são iniciados simultaneamente e fazem o mesmo caminho de pasta. A correção é adicionar if (errno != EEXIST) { status = -1; }quando o mkdir falhar.
Mudar
2
@Switch: Obrigado. Esse é o problema de usar stat()antes mkdir(); é um problema TOCTOU (tempo de verificação, tempo de uso). Tentei acertar o bug com um script de shell executando 13 processos em segundo plano, criando o mesmo caminho de 29 elementos, e não consegui acertá-lo. Então eu hackeei o programa de teste para fazer o fork 20 vezes e fiz cada criança tentar, e isso acertou o bug. O código fixo terá if (mkdir(path, mode) != 0 && errno != EEXIST) status = -1;. Isso não mostra o bug.
Jonathan Leffler
2
@DavidMerinos: são cabeçalhos ( jlss.h, emalloc.h), não bibliotecas. No entanto, o código está disponível em meus SOQ (Perguntas Stack Overflow) repositório no GitHub como arquivos jlss.h, emalloc.ce emalloc.hno src / libsoq sub-diretório. Você vai precisar posixver.htambém, e alguns outros ( debug.h, stderr.c, stderr.h- eu acho que é isso, mas o que você precisa todos devem estar nesse diretório).
Jonathan Leffler
157

Fácil com Boost.Filesystem: create_directories

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

Retorna: truese um novo diretório foi criado, caso contrário false.

Benoît
fonte
9
Bem, a maioria das bibliotecas boost são apenas de cabeçalho, o que significa que não há sobrecarga além do que você usa. No caso de Boost.Filesystem, ele requer compilação. No meu disco, a biblioteca compilada pesa ~ 60 KB.
Benoît,
1
@Lipis: especifique qual é o seu sistema embarcado. Eu acredito que ele deve estar disponível em quase todas as distribuições Linux.
Benoît,
4
Em relação aos compiladores C ++ 11 mencionados por @danijar, o comentário aqui deixou mais claro: 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 não é apenas cabeçalho: stackoverflow.com/questions/13604090/…
ftvs 01 de
2
IMHO: Em qualquer projeto meu que pretende fazer algo significativo e resistir ao teste do tempo, vale a pena compilar para ter um conjunto de ferramentas padronizadas incrivelmente úteis e poderosas como boost. Uma tonelada disso já fez seu caminho para o C ++ padrão, certamente com mais por vir. Experimente, persista, você se beneficiará se tiver necessidades mais do que triviais e não quiser reinventar a roda. :-)
moodboom
42
system("mkdir -p /tmp/a/b/c")

é o caminho mais curto que posso imaginar (em termos de comprimento de código, não necessariamente tempo de execução).

Não é multiplataforma, mas funcionará no Linux.

ChristopheD
fonte
1
SE você vai dar a solução como um comando shell, seria bom mencionar system (3)
dmckee --- ex-moderador gatinho
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);

A partir daqui . Você pode ter que fazer mkdirs separados para / tmp, / tmp / a, / tmp / a / b / e então / tmp / a / b / c porque não há um equivalente do sinalizador -p na API C. Certifique-se de ignorar o erro EEXISTS enquanto estiver fazendo os de nível superior.

Paul Tomblin
fonte
Curiosidade: pelo menos Solaris e HP / UX têm mkdirp (), embora claramente não seja ideal para portabilidade.
Martin Carpenter
esse é o ponto ... que eu não quero chamar todas essas funções separadamente.
Lipis
Chamar mkdir algumas vezes será muito, muito mais rápido do que chamar o sistema uma vez.
Paul Tomblin
Não entendo o que você está sugerindo: ligar para mkdir 4 vezes com argumentos diferentes? ("/tmp/",...), ("/tmp/a/",...), ("/tmp/a/b/",...),("/tmp/a/b/c/",...)
Antonio
1
Novamente, é bastante trivial fazer a mesma chamada três vezes. O objetivo é fornecer às pessoas informações suficientes para que possam escrever o código, e não escrever o código para elas.
Paul Tomblin
25

Aqui está meu exemplo de código (funciona para Windows e 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;
}

Uso:

$ makePath 1/2 folderA/folderB/folderC
creating 1/2 ... OK
creating folderA/folderB/folderC ... OK
Maxim Suslov
fonte
Apoio esse comentário! (Surpreendentemente) não é uma tarefa trivial encontrar uma maneira C ++ portátil de criar um diretório. Esta resposta precisa de mais votos positivos.
Manuel Lafond
1
No Windows, isDirExist não funciona se o caractere final for uma barra invertida. Sempre retorna falso. Eu tenho que modificar o código para: std :: string dirPath (path); while ('\\' == * dirPath.rbegin ()) dirPath.pop_back (); ... e então, é claro, passe dirPath.c_str () na chamada para _stat.
MiloDC
A API do Windows tem "nomes não ANSI para compatibilidade" para stat(relacionado a __STDC__) sem necessidade de teste de pré-compilador.
Sandburg
18

Deve-se notar que a partir da interface do sistema de arquivos C ++ 17 faz parte da biblioteca padrão. Isso significa que é possível ter o seguinte para criar diretórios:

#include <filesystem>

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

Mais informações aqui: https://en.cppreference.com/w/cpp/filesystem/create_directory

Além disso, com o gcc, é necessário "-std = c ++ 17" para CFLAGS. E "-lstdc ++ fs" para LDLIBS. O último potencialmente não será necessário no futuro.

mcsim
fonte
Também deve funcionar com Visual C ++ novo e "/ std: c ++ mais recente". Consulte: blogs.msdn.microsoft.com/vcblog/2018/05/07/… e developersercommunity.visualstudio.com/content/problem/296680/…
Ron Burk
9

Isso é semelhante ao anterior, mas funciona para a frente através da string em vez de recursivamente para trás. Deixa errno com o valor correto para a última falha. Se houver uma barra inicial, haverá um tempo extra no loop que poderia ter sido evitado por meio de um find_first_of () fora do loop ou detectando o / e definindo pre em 1. A eficiência é a mesma se formos configurados por um primeiro loop ou uma chamada de pré-loop, e a complexidade seria (ligeiramente) maior ao usar a chamada de pré-loop.

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

Você disse "C ++", mas todos aqui parecem estar pensando "Bash shell".

Verifique o código-fonte do gnu mkdir; então você pode ver como implementar os comandos do shell em C ++.

Jason Cohen
fonte
Bem, o sistema ("mkdir ...") deve fazer o truque no Linux. Porém, não é multiplataforma.
ChristopheD
Eu apoio o que @MartinCarpenter diz
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;
}
Marca
fonte
é a melhor solução para mim! Obrigado!)))
neo
talvez seja uma questão de despejo, mas qual é o prefixo "::" antes de mkdir?
Rayee Roded de
1
Encontrada a resposta, o :: garante que a resolução ocorra no namespace global stackoverflow.com/questions/4269034/…
Rayee Roded
4

Então, eu preciso mkdirp()hoje e achei as soluções nesta página muito complicadas. Portanto, escrevi um trecho bastante curto, que pode ser facilmente copiado para outras pessoas que se depararem com este tópico e me perguntem por que precisamos de tantas linhas de código.

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

Se você não gosta de lançar const e modificar temporariamente a string, basta fazer um strdup()e free()depois.

Jonasfj
fonte
Postado na essência também, então não me esqueço de onde colocarei da próxima vez que precisar :) gist.github.com/jonasfj/7797272
jonasfj
2
É ruim tentar modificar uma string que é passada como uma constante. À parte, é provável que leve a falhas dramáticas se alguma vez for transmitido um literal de string.
Jonathan Leffler
2
Perfeitamente certo ... isso é realmente ruim ... provavelmente forte tornaria isso melhor ...
jonasfj
3

Uma vez que esta postagem está no topo do ranking do Google para "Criar árvore de diretórios", postarei uma resposta que funcionará para Windows - funcionará usando a API Win32 compilada para UNICODE ou MBCS. Isso é transferido do código de Mark acima.

Como estamos trabalhando com o Windows, os separadores de diretório são barras invertidas, não barras normais. Se você preferir barras, mude '\\'para'/'

Funcionará com:

c:\foo\bar\hello\world

e

c:\foo\bar\hellp\world\

(ou seja: não precisa de barra final, então você não precisa verificar).

Antes de dizer "Basta usar SHCreateDirectoryEx () no Windows", observe que SHCreateDirectoryEx () está obsoleto e pode ser removido a qualquer momento de versões futuras do 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;
}
Andy
fonte
Um pequeno mod - se o caminho contém barras invertidas, isso não funciona. Aqui: `LPCTSTR szLast = _tcsrchr (szPathTree, '\\');` Você só precisa adicionar isto: `` `if (nullptr == szLast) {szLast = _tcsrchr (szPathTree, '/'); } `` `
Den-Jason
1
Obrigado pela informação. O que acontece se um caminho for misto? ou seja: c:\this\is\a/mixed/path\of\slashesNormalmente as barras do Windows são barras invertidas. O que deve acontecer é que o chamador deve limpar o caminho e garantir que todas as barras estejam corretas antes de chamar esse método.
Andy
3

Eu sei que é uma pergunta antiga, mas aparece no topo dos resultados de pesquisa do Google e as respostas fornecidas aqui não estão realmente em C ++ ou são um pouco complicadas.

Observe que no meu exemplo, createDirTree () é muito simples porque todo o trabalho pesado (verificação de erros, validação de caminho) precisa ser feito por createDir () de qualquer maneira. Além disso, createDir () deve retornar true se o diretório já existir ou se tudo isso não funcionar.

Veja como eu faria isso em 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;
}

É claro que a função createDir () será específica do sistema e já existem exemplos suficientes em outras respostas de como escrevê-la para Linux, então decidi pular.

Tom
fonte
1

Se dir não existir, crie-o:

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

Muitas abordagens foram descritas aqui, mas a maioria delas precisa de codificação rígida de seu caminho em seu código. Existe uma solução fácil para esse problema, usando QDir e QFileInfo, duas classes do framework Qt. Como você já está no ambiente Linux, deve ser fácil usar o Qt.

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

Certifique-se de ter acesso de gravação a esse caminho.

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

touch /dir/to/the/file/thefile.ending
Tim Cooper
fonte
a -popção é o que procuro. Obrigado!
asgs de
0

Aqui está a função recursiva C / C ++ que usa dirname()para percorrer de baixo para cima a árvore de diretórios. Ele irá parar assim que encontrar um ancestral existente.

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

Os outros deram a resposta certa, mas pensei em demonstrar outra coisa legal que você pode fazer:

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

Irá criar os seguintes caminhos:

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

As chaves permitem que você crie vários diretórios de uma vez no mesmo nível da hierarquia, enquanto a -popção significa "criar diretórios pais conforme necessário".

rmeador
fonte
depois de ver a resposta de Paul, percebo que eu (e muitas outras pessoas)
entendi
Se alguém puder simplesmente atualizar isso mudando para o sistema ("mkdir -p / tmp / a / {b, c} / d"), porque as perguntas não são sobre como fazer isso no shell .. mas através do C ++.
Lipis
Eu acho que {a, b} funcionará em shells derivados de sh e csh. Não tenho certeza se funcionará em um comando system (), no entanto.
Paul Tomblin
1
@Lipis: fazer isso via system () não é uma boa solução para a pergunta do OP. @Andy: Eu nunca havia considerado isso antes, mas acabei de testar substituindo "mkdir -p" por "echo" e ele imprime "/ tmp / a / b / d / tmp / a / c / d", o que sugere que é o shell que está fazendo isso, não mkdir.
rmeador
@rmeador: se não for uma boa solução, você tem alguma outra sugestão? Eu quero fazer isso através do C ++ ... esse é o meu problema, não como fazer isso através do shell ..
Lipis