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.