Come posso creare un albero di directory in C ++ / Linux?


109

Voglio un modo semplice per creare più directory in C ++ / Linux.

Ad esempio, voglio salvare un file lola.file nella directory:

/tmp/a/b/c

ma se le directory non ci sono, voglio che vengano create automaticamente. Un esempio funzionante sarebbe perfetto.


C ++ non ha alcuna funzionalità incorporata per creare directory e alberi di per sé . Dovrai usare C e le chiamate di sistema o una libreria esterna come Boost. C e le chiamate di sistema dipenderanno dalla piattaforma.
jww

6
@noloader Grazie mille amico .. ma penso di aver ricevuto la mia risposta dopo 4 anni, come puoi vedere qui sotto in 13 modi diversi ...
Lipis

Sì, sono rimasto sorpreso che nessuno abbia dichiarato esplicitamente che non puoi farlo in C ++ (supponendo che tu voglia un metodo portatile in C ++ che funzionasse su Linux). Ma probabilmente lo sapevi;). Tuttavia, c'erano molti buoni suggerimenti per il codice C non portatile.
jww

Che cos'è "C ++ / Linux"?
Gare di leggerezza in orbita

3
@LightnessRacesinOrbit Sono i miei anni universitari in C ++ su Linux :)
Lipis

Risposte:


59

Con C ++ 17 o versioni successive, c'è l'intestazione standard <filesystem>con funzione std::filesystem::create_directories che dovrebbe essere utilizzata nei moderni programmi C ++. Le funzioni standard C ++ non hanno l'argomento dei permessi espliciti (modalità) specifici di POSIX, però.

Tuttavia, ecco una funzione C che può essere compilata con i compilatori 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 */

Le macro STRDUP()e FREE()sono versioni di controllo degli errori di strdup()e free(), dichiarate in emalloc.h(e implementate in emalloc.ce estrdup.c). L' "sysstat.h"intestazione riguarda le versioni non funzionanti <sys/stat.h> e può essere sostituita dai <sys/stat.h>moderni sistemi Unix (ma c'erano molti problemi nel 1990). E "mkpath.h"dichiara mkpath().

Il cambiamento tra v1.12 (versione originale della risposta) e v1.13 (versione modificata della risposta) è stato il test per EEXISTin do_mkdir(). Questo è stato segnalato come necessario da Switch : grazie, Switch. Il codice di test è stato aggiornato e ha riprodotto il problema su un MacBook Pro (Intel Core i7 a 2,3 GHz, con Mac OS X 10.7.4) e suggerisce che il problema è stato risolto nella revisione (ma il test può solo mostrare la presenza di bug , mai la loro assenza). Il codice mostrato è ora v1.16; sono state apportate modifiche estetiche o amministrative dalla v1.13 (come l'uso al mkpath.hposto di jlss.he includere <unistd.h>incondizionatamente solo nel codice di test). È ragionevole sostenere che "sysstat.h"dovrebbe essere sostituito da a <sys/stat.h>meno che non si disponga di un sistema insolitamente recalcitrante.

(Con la presente ti viene concesso il permesso di utilizzare questo codice per qualsiasi scopo con attribuzione.)

Questo codice è disponibile nel mio repository SOQ (Stack Overflow Questions) su GitHub come file mkpath.ce mkpath.h(ecc.) Nella sottodirectory src / so-0067-5039 .


2
È sicuramente più veloce del sistema. Il sistema ha molte spese generali coinvolte. Fondamentalmente, il processo deve essere forkato, quindi devono essere caricati almeno due binari (uno sarà probabilmente già nella cache), su cui sarà ancora un altro fork dell'altro, ...
ypnos

1
Dimenticavo: E poi "mkdir -p" farà almeno lo stesso del codice postato sopra!
ypnos

7
C'è una sottile condizione di gara in questo codice che ho effettivamente colpito. Accade solo quando più programmi si avviano contemporaneamente e creano lo stesso percorso di cartella. La soluzione è aggiungere if (errno != EEXIST) { status = -1; }quando mkdir fallisce.
Cambia il

2
@ Switch: grazie. Questo è il problema con l'utilizzo stat()prima mkdir(); è un problema di TOCTOU (time of check, time of use). Ho provato a solleticare il bug con uno script di shell che esegue 13 processi in background creando lo stesso percorso di 29 elementi e non sono riuscito a colpirlo. Poi ho hackerato il programma di test per eseguire il fork 20 volte e far provare a ogni bambino, e questo è riuscito a colpire il bug. Il codice fisso avrà if (mkdir(path, mode) != 0 && errno != EEXIST) status = -1;. Questo non mostra il bug.
Jonathan Leffler

2
@DavidMerinos: sono intestazioni ( jlss.h, emalloc.h), non librerie. Tuttavia, il codice è disponibile nel mio SOQ (Stack Overflow domande) repository su GitHub come file jlss.h, emalloc.ce emalloc.hnella / libsoq src sub-directory. Avrete bisogno di posixver.htroppo, e pochi altri ( debug.h, stderr.c, stderr.h- io credo che sia così, ma quello che vi serve dovrebbero essere tutti in quella directory).
Jonathan Leffler

157

Facile con Boost.Filesystem: create_directories

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

Restituisce: truese è stata creata una nuova directory, altrimenti false.


9
Bene, la maggior parte delle librerie boost sono solo di intestazione, il che significa che non c'è overhead oltre a quello che usi. Nel caso di Boost.Filesystem, richiede comunque la compilazione. Sul mio disco, la libreria compilata pesa ~ 60 KB.
Benoît

1
@ Lipis: specifica qual è il tuo sistema embedded. Credo che dovrebbe essere disponibile su quasi tutte le distribuzioni Linux.
Benoît

4
Per quanto riguarda i compilatori C ++ 11 menzionati da @danijar, il commento qui ha reso più chiaro: 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 non è solo intestazioni: stackoverflow.com/questions/13604090/...
FTVS

2
IMHO: In ogni mio progetto che intende fare qualcosa di significativo e resistere alla prova del tempo, vale la pena compilare un set di strumenti standardizzati incredibilmente utili e potenti come boost. Un sacco di esso si è già fatto strada nel C ++ standard, sicuramente con altri in arrivo. Provalo, resta fedele, ne trarrai vantaggio se hai esigenze più che banali e non vuoi reinventare la ruota. :-)
moodboom

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

è il modo più breve a cui posso pensare (in termini di lunghezza del codice, non necessariamente tempo di esecuzione).

Non è multipiattaforma ma funzionerà sotto Linux.


1
SE
intendi

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

Da qui . Potrebbe essere necessario eseguire mkdir separati per / tmp, / tmp / a, / tmp / a / b / e quindi / tmp / a / b / c perché non esiste un equivalente del flag -p nell'api C. Assicurati di ignorare gli errori EEXISTS mentre stai facendo quelli di livello superiore.


Curiosità: almeno Solaris e HP / UX hanno mkdirp (), anche se chiaramente non è ottimale per la portabilità.
Martin Carpenter

questo è il punto .. che non voglio chiamare tutte queste funzioni separatamente.
Lipis

Chiamare mkdir alcune volte sarà molto, molto più veloce che chiamare il sistema una volta.
Paul Tomblin

Non capisco cosa stai suggerendo: chiamare mkdir 4 volte con argomenti diversi? ("/tmp/",...), ("/tmp/a/",...), ("/tmp/a/b/",...),("/tmp/a/b/c/",...)
Antonio

1
Di nuovo, è abbastanza banale fare la stessa chiamata tre volte. Il punto è dare alle persone informazioni sufficienti per poter scrivere il codice, non per scrivere il codice per loro.
Paul Tomblin

25

Ecco il mio esempio di codice (funziona sia per Windows che per 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

Secondo quel commento! Non è (sorprendentemente) un compito banale trovare un modo C ++ portatile per creare una directory. Questa risposta richiede più voti positivi.
Manuel Lafond

1
In Windows, isDirExist non funziona se il carattere finale è una barra rovesciata. Restituisce sempre false. Devo modificare il codice in: std :: string dirPath (path); while ('\\' == * dirPath.rbegin ()) dirPath.pop_back (); ... e poi, ovviamente, passa dirPath.c_str () nella chiamata a _stat.
MiloDC

L'API di Windows ha "nomi non ANSI per compatibilità" per stat(correlato a __STDC__) nessuna necessità del test precompilatore.
Sandburg

18

Va notato che a partire dall'interfaccia del filesystem C ++ 17 fa parte della libreria standard. Ciò significa che si può avere quanto segue per creare directory:

#include <filesystem>

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

Maggiori informazioni qui: https://en.cppreference.com/w/cpp/filesystem/create_directory

Inoltre, con gcc, è necessario "-std = c ++ 17" per CFLAGS. E "-lstdc ++ fs" in LDLIBS. Quest'ultimo potenzialmente non sarà richiesto in futuro.


Dovrebbe funzionare anche con Visual C ++ abbastanza nuovo e "/ std: c ++ latest". Vedere: blogs.msdn.microsoft.com/vcblog/2018/05/07/… e Developercommunity.visualstudio.com/content/problem/296680/…
Ron Burk

9

Questo è simile al precedente ma funziona in avanti attraverso la stringa invece che ricorsivamente all'indietro. Lascia errno con il valore giusto per l'ultimo errore. Se c'è uno slash iniziale, c'è un tempo extra attraverso il loop che avrebbe potuto essere evitato tramite un find_first_of () fuori dal loop o rilevando il leading / e impostando pre a 1. L'efficienza è la stessa se veniamo impostati da un primo loop o una chiamata pre-loop e la complessità sarebbe (leggermente) maggiore quando si utilizza la chiamata pre-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';

}

7

Hai detto "C ++" ma tutti qui sembrano pensare "shell Bash".

Controlla il codice sorgente di GNU mkdir; quindi puoi vedere come implementare i comandi della shell in C ++.


Bene, il sistema ("mkdir ...") dovrebbe fare il trucco su Linux. Tuttavia, non è multipiattaforma.
ChristopheD

Secondo quello che dice @MartinCarpenter
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;
}

è la soluzione migliore per me! Grazie!)))
neo

forse è una domanda stupida, ma qual è il prefisso "::" prima di mkdir?
Rayee Roded

1
Trovato la risposta, i :: assicura che la risoluzione si verifica dal namespace globale stackoverflow.com/questions/4269034/...
rayée Roded

4

Quindi ho bisogno mkdirp()oggi e ho trovato le soluzioni in questa pagina eccessivamente complicate. Quindi ho scritto uno snippet abbastanza breve, che può essere facilmente copiato per gli altri che si imbattono in questo thread, chiedendosi perché abbiamo bisogno di così tante righe di codice.

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 non ti piace il casting const e la modifica temporanea della stringa, fai semplicemente un strdup()e free()dopo.


Postato anche in un gist, quindi non dimentico dove lo metto la prossima volta che ne ho bisogno :) gist.github.com/jonasfj/7797272
jonasfj

2
È un male provare a modificare una stringa passata come costante. A parte tutto il resto, è probabile che porti a fallimenti drammatici se viene mai passato una stringa letterale.
Jonathan Leffler

2
Perfettamente giusto ... questo è effettivamente un male ... probabilmente strcpy lo renderebbe migliore ...
jonasfj

3

Poiché questo post è in cima alla classifica di Google per "Crea albero di directory", pubblicherò una risposta che funzionerà per Windows: funzionerà utilizzando l'API Win32 compilata per UNICODE o MBCS. Questo è portato dal codice di Mark sopra.

Dato che stiamo lavorando con Windows, i separatori di directory sono barre BACK, non barre in avanti. Se preferisci avere barre in avanti, '\\'passa a'/'

Funzionerà con:

c:\foo\bar\hello\world

e

c:\foo\bar\hellp\world\

(cioè: non ha bisogno della barra finale, quindi non devi controllarla.)

Prima di dire "Usa semplicemente SHCreateDirectoryEx () in Windows", nota che SHCreateDirectoryEx () è deprecato e potrebbe essere rimosso in qualsiasi momento dalle versioni future di 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;
}

Un piccolo mod: se il percorso contiene dei backslash non funziona. Qui: `LPCTSTR szLast = _tcsrchr (szPathTree, '\\');` Devi solo aggiungere questo: `` `if (nullptr == szLast) {szLast = _tcsrchr (szPathTree, '/'); } ``
Den-Jason

1
Grazie per le informazioni. Cosa succede se un percorso è misto? cioè: in c:\this\is\a/mixed/path\of\slashesgenere le barre di Windows sono barre rovesciate. Quello che dovrebbe accadere è che il chiamante dovrebbe disinfettare il percorso e assicurarsi che tutte le barre siano corrette prima di chiamare questo metodo.
Andy

3

So che è una vecchia domanda ma appare in alto nei risultati di ricerca di Google e le risposte fornite qui non sono realmente in C ++ o sono un po 'troppo complicate.

Si noti che nel mio esempio createDirTree () è molto semplice perché tutto il lavoro pesante (controllo degli errori, convalida del percorso) deve essere fatto comunque da createDir (). Inoltre createDir () dovrebbe restituire true se la directory esiste già o l'intera cosa non funzionerà.

Ecco come lo farei in 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;
}

Ovviamente la funzione createDir () sarà specifica del sistema e ci sono già abbastanza esempi in altre risposte su come scriverla per Linux, quindi ho deciso di saltarla.


1

Se dir non esiste, creala:

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

1

Sono stati descritti così tanti approcci qui, ma la maggior parte di essi richiede l'hard coding del percorso nel codice. Esiste una soluzione semplice per questo problema, utilizzando QDir e QFileInfo, due classi di framework Qt. Dato che sei già in ambiente Linux, dovrebbe essere facile usare Qt.

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

Assicurati di avere accesso in scrittura a quel percorso.


0
mkdir -p /dir/to/the/file

touch /dir/to/the/file/thefile.ending

l' -popzione è quello che sto cercando. Grazie!
asgs

0

Ecco la funzione ricorsiva C / C ++ che utilizza dirname()per attraversare dal basso verso l'alto l'albero delle directory. Si fermerà non appena troverà un antenato esistente.

#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

Gli altri ti hanno dato la risposta giusta, ma ho pensato di dimostrare un'altra bella cosa che puoi fare:

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

Creerà i seguenti percorsi:

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

Le parentesi graffe consentono di creare più directory contemporaneamente sullo stesso livello della gerarchia, mentre l' -popzione significa "creare directory padre secondo necessità".


dopo aver visto la risposta di Paul, mi rendo conto che io (e molte altre persone) ho frainteso la domanda ...
rmeador

Se qualcuno può semplicemente aggiornarlo passando al sistema ("mkdir -p / tmp / a / {b, c} / d"), perché le domande non riguardano farlo in shell .. ma attraverso C ++.
Lipis

Penso che {a, b} funzionerà sia con shell derivate da sh che con shell derivate da csh. Tuttavia, non sono sicuro che funzionerà con un comando system ().
Paul Tomblin,

1
@ Lipis: farlo tramite system () non è una buona soluzione alla domanda dell'OP. @ Anddy: non l'avevo mai considerato prima, ma l'ho appena testato sostituendo "mkdir -p" con "echo" e stampa "/ tmp / a / b / d / tmp / a / c / d", il che suggerisce che è la shell a farlo, non mkdir.
rmeador

@rmeador: se non è una buona soluzione, hai qualcos'altro da suggerire? Voglio farlo tramite C ++ ... questo è il mio problema non come farlo tramite shell ..
Lipis
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.