Jak mogę stworzyć drzewo katalogów w C ++ / Linux?


109

Chcę mieć łatwy sposób tworzenia wielu katalogów w C ++ / Linuxie.

Na przykład chcę zapisać plik lola.file w katalogu:

/tmp/a/b/c

ale jeśli nie ma katalogów, chcę, aby były tworzone automagicznie. Działający przykład byłby doskonały.


C ++ nie posiada żadnych wbudowanych urządzeń do tworzenia katalogów i drzew per se . Będziesz musiał użyć C i wywołań systemowych lub biblioteki zewnętrznej, takiej jak Boost. C i wywołania systemowe będą zależne od platformy.
— jww

6
@noloader Wielkie dzięki stary ... ale myślę, że po 4 latach otrzymałem odpowiedź, jak widać poniżej na 13 różnych sposobów ...
— Lipis

Tak, byłem zaskoczony, że nikt wyraźnie nie stwierdził, że nie możesz tego zrobić w C ++ (zakładając, że chcesz mieć przenośną metodę w C ++, która działałaby w systemie Linux). Ale pewnie o tym wiedziałeś;). Było jednak wiele dobrych sugestii dotyczących nieprzenośnego kodu C.
— jww

Co to jest „C ++ / Linux”?
— Wyścigi lekkości na orbicie

3
@LightnessRacesinOrbit To moje lata uniwersyteckie w C ++ na Linuksie :)
— Lipis

Odpowiedzi:


59

W C ++ 17 lub nowszym istnieje standardowy nagłówek <filesystem>z funkcją, std::filesystem::create_directories który powinien być używany we współczesnych programach C ++. Standardowe funkcje C ++ nie mają jednak specyficznego dla POSIX jawnego argumentu permissions (mode).

Jednak tutaj jest funkcja C, którą można skompilować za pomocą kompilatorów 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 */

Makra STRDUP()i FREE()są wersjami sprawdzającymi błędy programu strdup()i free(), zadeklarowanymi w emalloc.h(i zaimplementowanymi w emalloc.ci estrdup.c). Do "sysstat.h"oferty nagłówek z rozbitych wersjach <sys/stat.h> i może być zastąpiony <sys/stat.h>na nowoczesnych systemach Unix (ale było wiele problemów z powrotem w 1990 roku). I "mkpath.h"oświadczamkpath() .

Zmiana między wersją 1.12 (oryginalna wersja odpowiedzi) a wersją 1.13 (poprawiona wersja odpowiedzi) była testem EEXISTw do_mkdir(). Zwrócił na to uwagę Switch - dziękuję Switch. Kod testowy został zaktualizowany i odtworzył problem na MacBooku Pro (2,3 GHz Intel Core i7, z systemem Mac OS X 10.7.4) i sugeruje, że problem został rozwiązany w wersji (ale testy mogą tylko wykazać obecność błędów , nigdy ich nieobecność). Wyświetlany kod to teraz wersja 1.16; od wersji 1.13 wprowadzono zmiany kosmetyczne lub administracyjne (takie jak używanie mkpath.hzamiast jlss.hi <unistd.h>bezwarunkowe uwzględnianie tylko w kodzie testowym). Rozsądnie jest argumentować, że "sysstat.h"należy go zastąpić, <sys/stat.h>chyba że masz niezwykle oporny system.

(Otrzymujesz zgodę na użycie tego kodu w dowolnym celu z podaniem źródła).

Ten kod jest dostępny w moim repozytorium SOQ (Stack Overflow Questions) na GitHub jako pliki mkpath.ci mkpath.h(itp.) W podkatalogu src / so-0067-5039 .


2
Z pewnością jest szybszy niż system. System ma dużo narzutów. Zasadniczo proces musi zostać rozwidlony, a następnie załadować co najmniej dwa pliki binarne (jeden prawdopodobnie będzie już w pamięci podręcznej), z których jeden będzie kolejnym widelcem drugiego, ...
— ypnos

1
Zapomniałem: I wtedy "mkdir -p" zrobi co najmniej to samo, co zamieszczony powyżej kod!
— ypnos

7
W tym kodzie jest subtelny stan wyścigu, w który trafiłem. Dzieje się tak tylko wtedy, gdy wiele programów uruchamia się jednocześnie i tworzy tę samą ścieżkę do folderu. Poprawka polega na dodaniu, if (errno != EEXIST) { status = -1; }gdy mkdir zawiedzie.
— Przełącz

2
@Switch: Dzięki. To jest problem z używaniem stat()wcześniej mkdir(); jest to problem TOCTOU (czas sprawdzenia, czas użycia). Próbowałem łaskotać błąd skryptem powłoki uruchamiającym 13 procesów w tle, tworząc tę ​​samą 29-elementową ścieżkę i nie udało mi się go trafić. Następnie włamałem się do programu testowego, aby rozwidlić 20 razy i kazałem każdemu dziecku spróbować, a to udało się trafić w błąd. Naprawiony kod będzie miał if (mkdir(path, mode) != 0 && errno != EEXIST) status = -1;. To nie pokazuje błędu.
— Jonathan Leffler,

2
@DavidMerinos: to nagłówki ( jlss.h, emalloc.h), a nie biblioteki. Jednak kod jest dostępny w moim SOQ (przepełnienie stosu pytań) repozytorium na GitHub jako pliki jlss.h, emalloc.ca emalloc.hw libsoq / src podkatalogu. Będziesz też potrzebować posixver.hi kilka innych ( debug.h,stderr.c , stderr.h- Myślę, że to wszystko, ale to, czego potrzebujesz powinny być w tym katalogu).
— Jonathan Leffler

157

Łatwe dzięki Boost. create_directories

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

Zwraca: truejeśli utworzono nowy katalog, w przeciwnym razie false.


9
Cóż, większość bibliotek Boost obsługuje tylko nagłówki, co oznacza, że ​​nie ma narzutów poza tym, czego używasz. W przypadku Boost.Filesystem wymaga jednak kompilacji. Na moim dysku skompilowana biblioteka waży ~ 60KB.
— Benoît

1
@ Lipis: proszę sprecyzować, jaki jest twój system wbudowany. Uważam, że powinien być dostępny w prawie każdej dystrybucji Linuksa.
— Benoît

4
Jeśli chodzi o kompilatory C ++ 11, o których wspomniał @danijar, komentarz tutaj wyjaśnił to: 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 nie jest tylko nagłówkiem: stackoverflow.com/questions/13604090/ ...
— ftvs

2
IMHO: W każdym moim projekcie, który ma na celu zrobienie czegoś znaczącego i przetrwanie próby czasu, DOBRZE warto skompilować taki zestaw niezwykle przydatnych i potężnych standardowych narzędzi, jak boost. Mnóstwo tego już trafiło do standardowego C ++, a na pewno jeszcze więcej. Wypróbuj, trzymaj się tego, odniesiesz korzyści, jeśli masz więcej niż trywialne potrzeby i nie chcesz wymyślać koła na nowo. :-)
— moodboom

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

to najkrótszy sposób, jaki mogę sobie wyobrazić (pod względem długości kodu, niekoniecznie czasu wykonania).

Nie jest to platforma wieloplatformowa, ale będzie działać pod Linuksem.


1
JEŚLI zamierzasz podać rozwiązanie jako polecenie powłoki, dobrze byłoby wspomnieć o systemie (3)
— dmckee --- ex-moderator kitten

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

Od tutaj . Może być konieczne utworzenie oddzielnych katalogów mkdir dla / tmp, / tmp / a, / tmp / a / b /, a następnie / tmp / a / b / c, ponieważ nie ma odpowiednika opcji -p w interfejsie API C. Upewnij się i zignoruj ​​EEXISTS errno podczas wykonywania zadań wyższego poziomu.


Ciekawostka: przynajmniej Solaris i HP / UX mają mkdirp (), chociaż najwyraźniej nie jest to optymalne do przenoszenia.
— Martin Carpenter,

o to chodzi ... że nie chcę nazywać tych wszystkich funkcji osobno.
— Lipis

Wywołanie mkdir kilka razy będzie o wiele szybsze niż jednokrotne wywołanie systemu.
— Paul Tomblin,

Nie rozumiem, co sugerujesz: dzwonisz do mkdir 4 razy z różnymi argumentami? ("/tmp/",...), ("/tmp/a/",...), ("/tmp/a/b/",...),("/tmp/a/b/c/",...)
— Antonio

1
Ponownie, wykonanie tego samego połączenia trzy razy jest dość trywialne. Chodzi o to, aby dać ludziom wystarczającą ilość informacji, aby mogli napisać kod, a nie napisać kod za nich.
— Paul Tomblin

25

Oto mój przykład kodu (działa zarówno w systemie Windows, jak i 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;
}

Stosowanie:

$ makePath 1/2 folderA/folderB/folderC
creating 1/2 ... OK
creating folderA/folderB/folderC ... OK

Popieram ten komentarz! Znalezienie przenośnego sposobu w C ++ na utworzenie katalogu nie jest (zaskakująco) prostym zadaniem. Ta odpowiedź wymaga więcej głosów pozytywnych.
— Manuel Lafond

1
W systemie Windows isDirExist nie działa, jeśli końcowym znakiem jest ukośnik odwrotny. Zawsze zwraca fałsz. Muszę zmodyfikować kod do: std :: string dirPath (ścieżka); while ('\\' == * dirPath.rbegin ()) dirPath.pop_back (); ... a następnie oczywiście przekaż dirPath.c_str () w wywołaniu _stat.
— MiloDC,

Interfejs API systemu Windows ma „nazwy niezgodne z ANSI na potrzeby zgodności”, które stat(związane z __STDC__) nie wymagają testu prekompilatora.
— Sandburg

18

Należy zauważyć, że począwszy od systemu plików C ++ 17 interfejs jest częścią standardowej biblioteki. Oznacza to, że można utworzyć katalogi:

#include <filesystem>

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

Więcej informacji tutaj: https://en.cppreference.com/w/cpp/filesystem/create_directory

Dodatkowo, z gcc, trzeba "-std = c ++ 17" do CFLAGS. I „-lstdc ++ fs” do LDLIBS. Ten ostatni potencjalnie nie będzie potrzebny w przyszłości.


Powinien również działać z nowym, wystarczająco nowym Visual C ++ i "/ std: c ++ najnowszym". Zobacz: blogs.msdn.microsoft.com/vcblog/2018/05/07/… i developercommunity.visualstudio.com/content/problem/296680/…
— Ron Burk

9

Jest to podobne do poprzedniego, ale działa w przód przez łańcuch zamiast rekurencyjnie do tyłu. Pozostawia errno odpowiednią wartość dla ostatniej awarii. Jeśli występuje wiodący ukośnik, istnieje dodatkowy czas w pętli, którego można było uniknąć za pomocą funkcji find_first_of () poza pętlą lub przez wykrycie wiodącego / i ustawienie pre na 1. Wydajność jest taka sama, niezależnie od tego, czy zostaniemy skonfigurowani przez pierwsza pętla lub wywołanie przed pętlą, a złożoność byłaby (nieco) większa podczas korzystania z wywołania przed pętlą.

#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

Powiedziałeś „C ++”, ale wydaje się, że wszyscy tutaj myślą „powłoka bash”.

Sprawdź kod źródłowy GNU mkdir; wtedy możesz zobaczyć, jak zaimplementować polecenia powłoki w C ++.


Cóż, system ("mkdir ...") powinien załatwić sprawę na Linuksie. Nie jest to jednak platforma wieloplatformowa.
— ChristopheD

Po drugie, co mówi @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;
}

to dla mnie najlepsze rozwiązanie! Dziękuję!)))
— neo

może jest to pytanie dotyczące zrzutu, ale jaki jest przedrostek „::” przed mkdir?
— Rayee Roded

1
Znalazłem odpowiedź, :: zapewnia, że ​​rozdzielczość następuje z globalnej przestrzeni nazw stackoverflow.com/questions/4269034/ ...
— Rayee Roded

4

Więc potrzebuję mkdirp()dzisiaj, a rozwiązania na tej stronie okazały się zbyt skomplikowane. Dlatego napisałem dość krótki fragment, który można łatwo skopiować dla innych, którzy natkną się na ten wątek, i zastanawiam się, dlaczego potrzebujemy tak wielu wierszy kodu.

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

Jeśli nie lubisz rzucać const i tymczasowo modyfikować łańcucha, po prostu zrób a strdup()i free()potem.


Zamieszczono też w streszczeniu, więc nie zapomnę, gdzie go wstawię
— jonasfj

2
Jest złem próbować modyfikować ciąg, który jest przekazywany jako stała. Poza tym może doprowadzić do dramatycznych niepowodzeń, jeśli kiedykolwiek zostanie przekazany dosłowny ciąg.
— Jonathan Leffler

2
Doskonale zgadza się .... to faktycznie jest złe ... prawdopodobnie strcpy by to poprawił ...
— jonasfj

3

Ponieważ ten post zajmuje wysokie miejsce w Google w kategorii „Utwórz drzewo katalogów”, zamierzam opublikować odpowiedź, która będzie działać w systemie Windows - będzie działać przy użyciu interfejsu API Win32 skompilowanego dla UNICODE lub MBCS. Jest to przeniesione z powyższego kodu Marka.

Ponieważ jest to system Windows, z którym pracujemy, separatorami katalogów są ukośniki BACK, a nie ukośniki w przód. Jeśli wolisz ukośniki, zmień '\\'na'/'

Będzie działać z:

c:\foo\bar\hello\world

i

c:\foo\bar\hellp\world\

(tj .: nie wymaga końcowego ukośnika, więc nie musisz go sprawdzać).

Zanim powiesz „Po prostu użyj SHCreateDirectoryEx () w Windows”, zwróć uwagę, że SHCreateDirectoryEx () jest przestarzała i może zostać usunięta w dowolnym momencie z przyszłych wersji systemu 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;
}

Jeden mały mod - jeśli ścieżka zawiera odwrotne ukośniki, to nie działa. Tutaj: `LPCTSTR szLast = _tcsrchr (szPathTree, '\\');` Wystarczy dodać to: `` `` if (nullptr == szLast) {szLast = _tcsrchr (szPathTree, '/'); } ``
— Den-Jason

1
Dzięki za informację. Co się stanie, jeśli ścieżka jest mieszana? tj.: c:\this\is\a/mixed/path\of\slashesZazwyczaj ukośniki systemu Windows są ukośnikami odwrotnymi. Powinno się zdarzyć, że wywołujący powinien oczyścić ścieżkę i upewnić się, że wszystkie ukośniki są prawidłowe przed wywołaniem tej metody.
— Andy

3

Wiem, że to stare pytanie, ale pojawia się wysoko w wynikach wyszukiwania Google, a podane tutaj odpowiedzi nie są tak naprawdę w C ++ lub są nieco zbyt skomplikowane.

Zwróć uwagę, że w moim przykładzie createDirTree () jest bardzo proste, ponieważ całe podnoszenie ciężarów (sprawdzanie błędów, walidacja ścieżki) i tak musi być wykonane przez createDir (). Również createDir () powinno zwrócić true, jeśli katalog już istnieje lub całość nie zadziała.

Oto jak zrobiłbym to w 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;
}

Oczywiście funkcja createDir () będzie specyficzna dla systemu, aw innych odpowiedziach jest już wystarczająco dużo przykładów, jak napisać ją dla Linuksa, więc zdecydowałem się ją pominąć.


1

Jeśli katalog nie istnieje, utwórz go:

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

1

Opisano tutaj tak wiele podejść, ale większość z nich wymaga twardego zakodowania ścieżki do kodu. Istnieje proste rozwiązanie tego problemu, używając QDir i QFileInfo, dwóch klas frameworka Qt. Ponieważ jesteś już w środowisku Linux, korzystanie z Qt powinno być łatwe.

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

Upewnij się, że masz dostęp do zapisu w tej ścieżce.


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

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

-prozwiązaniem jest to, czego szukam. Dzięki!
— asgs

0

Oto rekurencyjna funkcja C / C ++, która wykorzystuje dirname()do przechodzenia z dołu do góry drzewa katalogów. Zatrzyma się, gdy tylko znajdzie istniejącego przodka.

#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

Inni dali ci właściwą odpowiedź, ale pomyślałem, że zademonstruję inną fajną rzecz, którą możesz zrobić:

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

Stworzy następujące ścieżki:

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

Nawiasy umożliwiają tworzenie wielu katalogów jednocześnie na tym samym poziomie hierarchii, podczas gdy -popcja oznacza „utwórz katalogi nadrzędne według potrzeb”.


po zobaczeniu odpowiedzi Paula zdaję sobie sprawę, że ja (i wiele innych osób) źle zrozumiałem pytanie ...
— rmeador

Jeśli ktoś może to po prostu zaktualizować, przechodząc na system ("mkdir -p / tmp / a / {b, c} / d"), ponieważ pytania nie dotyczą robienia tego w powłoce .. ale przez C ++.
— Lipis

Myślę, że {a, b} będzie działać zarówno w powłokach pochodzących z sh, jak i csh. Nie jestem jednak pewien, czy zadziała w poleceniu system ().
— Paul Tomblin,

1
@Lipis: robienie tego przez system () nie jest dobrym rozwiązaniem na pytanie OP. @Andy: Nigdy wcześniej tego nie brałem pod uwagę, ale właśnie przetestowałem to, zastępując „mkdir -p” słowem „echo” i wypisuje „/ tmp / a / b / d / tmp / a / c / d”, co sugeruje, że robi to powłoka, a nie mkdir.
— rmeador

@rmeador: jeśli to nie jest dobre rozwiązanie, czy masz coś innego do zaproponowania? Chcę to zrobić za pomocą C ++ ... to mój problem, a nie jak to zrobić przez powłokę ..
— Lipis
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.