Bagaimana cara membuat pohon direktori di C ++ / Linux?

109

Saya ingin cara mudah untuk membuat banyak direktori di C ++ / Linux.

Misalnya saya ingin menyimpan file lola.file di direktori:

/tmp/a/b/c

tetapi jika direktori tidak ada, saya ingin mereka dibuat secara otomatis. Contoh yang berhasil akan sempurna.

Lipis
sumber
C ++ tidak memiliki fasilitas built-in untuk membuat direktori dan pohon itu sendiri . Anda harus menggunakan C dan panggilan sistem atau pustaka eksternal seperti Boost. C dan panggilan sistem akan bergantung pada platform.
jww
6
@noloader Terima kasih banyak teman .. tapi saya pikir setelah 4 tahun saya cukup banyak mendapatkan jawaban saya seperti yang Anda lihat di bawah ini dalam 13 cara yang berbeda ...
Lipis
Ya, saya terkejut tidak ada yang secara eksplisit menyatakan Anda tidak dapat melakukannya di C ++ (dengan asumsi Anda menginginkan metode portabel di C ++ yang berfungsi di Linux). Tapi Anda mungkin tahu itu;). Ada banyak saran bagus untuk kode C non-portabel.
jww
Apa itu "C ++ / Linux"?
Balapan Ringan di Orbit
3
@LightnessRacesinOrbit Ini tahun-tahun universitas saya di C ++ di Linux :)
Lipis

Jawaban:

59

Dengan C ++ 17 atau yang lebih baru, ada header standar <filesystem>dengan fungsi std::filesystem::create_directories yang harus digunakan dalam program C ++ modern. Fungsi standar C ++ tidak memiliki argumen izin eksplisit (mode) spesifik POSIX.

Namun, inilah fungsi C yang dapat dikompilasi dengan kompiler 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 */

Makro STRDUP()dan FREE()merupakan versi pemeriksaan kesalahan dari strdup()dan free(), dideklarasikan di emalloc.h(dan diterapkan di emalloc.cdan estrdup.c). The "sysstat.h"penawaran header dengan versi rusak dari <sys/stat.h> dan dapat digantikan oleh <sys/stat.h>pada sistem Unix modern (tapi ada banyak masalah kembali pada tahun 1990). Dan "mkpath.h"menyatakan mkpath().

Perubahan antara v1.12 (versi asli dari jawaban) dan v1.13 (versi jawaban yang diubah) adalah ujian untuk EEXISTdi do_mkdir(). Hal ini ditunjukkan seperlunya oleh Switch - terima kasih, Switch. Kode tes telah ditingkatkan dan mereproduksi masalah pada MacBook Pro (2,3GHz Intel Core i7, menjalankan Mac OS X 10.7.4), dan menyarankan bahwa masalah telah diperbaiki dalam revisi (tetapi pengujian hanya dapat menunjukkan adanya bug , tidak pernah ketidakhadiran mereka). Kode yang ditampilkan sekarang adalah v1.16; ada perubahan kosmetik atau administratif yang dibuat sejak v1.13 (seperti penggunaan sebagai mkpath.hganti jlss.hdan menyertakan <unistd.h>tanpa syarat dalam kode pengujian saja). Masuk akal untuk berpendapat bahwa "sysstat.h"harus diganti <sys/stat.h>kecuali jika Anda memiliki sistem bandel yang tidak biasa.

(Anda dengan ini diberi izin untuk menggunakan kode ini untuk tujuan apa pun dengan atribusi.)

Kode ini tersedia di repositori SOQ (Stack Overflow Questions) saya di GitHub sebagai file mkpath.cdan mkpath.h(dll.) Di sub-direktori src / so-0067-5039 .

Jonathan Leffler
sumber
2
Ini pasti lebih cepat dari sistem. Sistem memiliki banyak overhead yang terlibat. Pada dasarnya, prosesnya harus bercabang, maka setidaknya dua binari harus dimuat (satu mungkin sudah ada di cache), di mana akan menjadi percabangan lain dari yang lain, ...
ypnos
1
Saya lupa: Kemudian "mkdir -p" akan melakukan hal yang sama seperti kode yang diposting di atas!
ypnos
7
Ada kondisi balapan halus dalam kode ini yang sebenarnya saya pukul. Ini hanya terjadi ketika beberapa program memulai secara bersamaan dan membuat jalur folder yang sama. Perbaikannya adalah menambahkan if (errno != EEXIST) { status = -1; }ketika mkdir gagal.
Beralih
2
@Switch: Terima kasih. Itulah masalah menggunakan stat()sebelumnya mkdir(); ini adalah masalah TOCTOU (waktu pemeriksaan, waktu penggunaan). Saya mencoba menggelitik bug dengan skrip shell yang menjalankan 13 proses di latar belakang yang menciptakan jalur 29 elemen yang sama, dan tidak berhasil melakukannya. Kemudian saya meng-hack program uji coba ke garpu 20 kali dan meminta setiap anak mencobanya, dan itu berhasil mengenai bug. Kode tetap akan memiliki if (mkdir(path, mode) != 0 && errno != EEXIST) status = -1;. Itu tidak menunjukkan bugnya.
Jonathan Leffler
2
@DavidMerinos: mereka adalah header ( jlss.h, emalloc.h), bukan perpustakaan. Namun, kode ini tersedia di repositori SOQ (Stack Overflow Questions) saya di GitHub sebagai file jlss.h, emalloc.cdan emalloc.hdi sub-direktori src / libsoq . Anda harus posixver.hjuga, dan beberapa orang lainnya ( debug.h, stderr.c, stderr.h- saya pikir itu saja, tapi apa yang Anda perlu semua harus dalam direktori itu).
Jonathan Leffler
157

Mudah dengan Boost.Filesystem: create_directories

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

Mengembalikan: truejika direktori baru dibuat, sebaliknya false.

Benoît
sumber
9
Nah, sebagian besar pustaka boost hanya untuk header, yang berarti tidak ada overhead selain yang Anda gunakan. Dalam kasus Boost.Filesystem, itu membutuhkan kompilasi. Di disk saya, perpustakaan yang dikompilasi berbobot ~ 60KB.
Benoît
1
@Lipis: harap jelaskan apa sistem tertanam Anda. Saya percaya ini harus tersedia di hampir semua distribusi linux.
Benoît
4
Mengenai kompiler C ++ 11 yang disebutkan oleh @danijar, komentar di sini membuatnya lebih jelas: 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 tidak hanya untuk header: stackoverflow.com/questions/13604090/…
ftvs
2
IMHO: Dalam proyek saya apa pun yang dimaksudkan untuk melakukan sesuatu yang signifikan dan bertahan dalam ujian waktu, sangat layak untuk dikompilasi untuk memiliki seperangkat alat standar yang sangat berguna dan kuat seperti dorongan. Satu ton itu telah masuk ke standar C ++, pasti dengan lebih banyak yang akan datang. Cobalah, pertahankan, Anda akan mendapat manfaat jika Anda memiliki lebih dari sekadar kebutuhan yang sepele dan tidak ingin menciptakan roda. :-)
moodboom
42
system("mkdir -p /tmp/a/b/c")

adalah cara terpendek yang dapat saya pikirkan (dalam hal panjang kode, belum tentu waktu eksekusi).

Ini bukan lintas platform tetapi akan berfungsi di Linux.

ChristopheD
sumber
1
JIKA Anda akan memberikan solusi sebagai perintah shell, akan lebih baik untuk menyebutkan system (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);

Dari sini . Anda mungkin harus melakukan mkdir terpisah untuk / tmp, / tmp / a, / tmp / a / b / dan kemudian / tmp / a / b / c karena tidak ada yang setara dengan flag -p di C api. Pastikan dan abaikan errno EEXISTS saat Anda melakukan yang tingkat atas.

Paul Tomblin
sumber
Fakta menarik: Setidaknya Solaris dan HP / UX memiliki mkdirp (), meskipun jelas tidak optimal untuk portabilitas.
Martin Carpenter
itulah intinya .. bahwa saya tidak ingin memanggil semua fungsi ini secara terpisah.
Lipis
Memanggil mkdir beberapa kali akan jauh lebih cepat daripada sistem panggilan sekali.
Paul Tomblin
Saya tidak mengerti apa yang Anda sarankan: menelepon mkdir 4 kali dengan argumen berbeda? ("/tmp/",...), ("/tmp/a/",...), ("/tmp/a/b/",...),("/tmp/a/b/c/",...)
Antonio
1
Sekali lagi, sangat mudah untuk melakukan panggilan yang sama sebanyak tiga kali. Intinya adalah memberikan informasi yang cukup kepada orang-orang sehingga mereka dapat menulis kode, bukan menulis kode untuk mereka.
Paul Tomblin
25

Berikut adalah contoh kode saya (berfungsi untuk Windows dan 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;
}

Pemakaian:

$ makePath 1/2 folderA/folderB/folderC
creating 1/2 ... OK
creating folderA/folderB/folderC ... OK
Maxim Suslov
sumber
Saya setuju dengan komentar itu! Ini (secara mengejutkan) bukanlah tugas yang sepele untuk menemukan cara C ++ portabel untuk membuat direktori. Jawaban ini membutuhkan lebih banyak suara positif.
Manuel Lafond
1
Di bawah Windows, isDirExist tidak berfungsi jika karakter di belakangnya adalah garis miring terbalik. Selalu mengembalikan nilai salah. Saya harus mengubah kode menjadi: std :: string dirPath (path); while ('\\' == * dirPath.rbegin ()) dirPath.pop_back (); ... dan kemudian, tentu saja, berikan dirPath.c_str () dalam panggilan ke _stat.
MiloDC
Windows API memiliki "Nama non-ANSI untuk kompatibilitas" karena stat(terkait dengan __STDC__) tidak perlu uji precompiler.
Sandburg
18

Perlu dicatat bahwa mulai dari antarmuka sistem file C ++ 17 adalah bagian dari pustaka standar. Ini berarti bahwa seseorang dapat memiliki berikut ini untuk membuat direktori:

#include <filesystem>

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

Info lebih lanjut di sini: https://en.cppreference.com/w/cpp/filesystem/create_directory

Selain itu, dengan gcc, seseorang perlu "-std = c ++ 17" ke CFLAGS. Dan "-lstdc ++ fs" ke LDLIBS. Potensi yang terakhir tidak akan dibutuhkan di masa depan.

mcsim.dll
sumber
Juga harus bekerja dengan Visual C ++ yang cukup baru dan "/ std: c ++ latest". Lihat: blogs.msdn.microsoft.com/vcblog/2018/05/07/… dan developercommunity.visualstudio.com/content/problem/296680/…
Ron Burk
9

Ini mirip dengan yang sebelumnya tetapi bekerja maju melalui string alih-alih mundur secara rekursif. Meninggalkan errno dengan nilai yang tepat untuk kegagalan terakhir. Jika ada garis miring di depan, ada waktu tambahan melalui loop yang dapat dihindari melalui satu find_first_of () di luar loop atau dengan mendeteksi leading / dan pengaturan pra ke 1. Efisiensinya sama baik kita disiapkan oleh a loop pertama atau panggilan pra-loop, dan kompleksitas akan (sedikit) lebih tinggi saat menggunakan panggilan pra-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
sumber
7

Anda mengatakan "C ++" tetapi semua orang di sini tampaknya berpikir "Bash shell."

Lihat kode sumber ke gnu mkdir; lalu Anda dapat melihat cara mengimplementasikan perintah shell di C ++.

Jason Cohen
sumber
Nah sistem ("mkdir ...") harus melakukan trik di linux. Ini bukan lintas platform.
ChristopheD
Saya setuju dengan apa yang dikatakan @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;
}
Menandai
sumber
itu solusi terbaik untuk saya! Terima kasih!)))
neo
mungkin ini pertanyaan dump, tapi apa awalan "::" sebelum mkdir?
Rayee Roded
1
Menemukan jawabannya, :: memastikan bahwa resolusi terjadi dari namespace global stackoverflow.com/questions/4269034/…
Rayee Roded
4

Jadi saya perlu mkdirp()hari ini, dan menemukan solusi di halaman ini terlalu rumit. Oleh karena itu saya menulis potongan yang cukup pendek, yang mudah disalin bagi orang lain yang tersandung pada utas ini heran mengapa kita membutuhkan begitu banyak baris kode.

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

Jika Anda tidak menyukai casting const dan memodifikasi string untuk sementara, lakukan a strdup()dan free()setelahnya.

jonasfj
sumber
Diposting ke intinya juga, jadi saya tidak lupa di mana saya meletakkannya lain kali jika saya membutuhkannya :) gist.github.com/jonasfj/7797272
jonasfj
2
Mencoba memodifikasi string yang diberikan sebagai konstanta adalah tindakan yang jahat. Semua selain itu, kemungkinan besar akan menyebabkan kegagalan dramatis jika pernah diteruskan string literal.
Jonathan Leffler
2
Benar sekali .... ini sebenarnya buruk ... mungkin strcpy akan membuat ini lebih baik ...
jonasfj
3

Karena posting ini memiliki peringkat tinggi di Google untuk "Buat Pohon Direktori", saya akan memposting jawaban yang akan bekerja untuk Windows - ini akan bekerja menggunakan Win32 API yang dikompilasi untuk UNICODE atau MBCS. Ini porting dari kode Mark di atas.

Karena ini adalah Windows yang kami gunakan, pemisah direktori adalah garis miring BACK, bukan garis miring ke depan. Jika Anda lebih suka menggunakan garis miring ke depan, ubah '\\'ke'/'

Ini akan bekerja dengan:

c:\foo\bar\hello\world

dan

c:\foo\bar\hellp\world\

(yaitu: tidak perlu garis miring, jadi Anda tidak perlu memeriksanya.)

Sebelum mengatakan "Cukup gunakan SHCreateDirectoryEx () di Windows", perhatikan bahwa SHCreateDirectoryEx () sudah usang dan dapat dihapus kapan saja dari versi Windows yang akan datang.

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
sumber
Satu mod kecil - jika jalur berisi garis miring terbalik, ini tidak berfungsi. Di sini: `LPCTSTR szLast = _tcsrchr (szPathTree, '\\');` Anda hanya perlu menambahkan ini: `` if (nullptr == szLast) {szLast = _tcsrchr (szPathTree, '/'); } ``
Den-Jason
1
Terimakasih atas infonya. Apa yang terjadi jika jalur dicampur? yaitu: c:\this\is\a/mixed/path\of\slashesBiasanya garis miring Windows adalah garis miring terbalik. Apa yang harus terjadi adalah pemanggil harus membersihkan jalur dan memastikan semua garis miring tepat sebelum memanggil metode ini.
Andy
3

Saya tahu itu pertanyaan lama tetapi muncul tinggi di hasil pencarian google dan jawaban yang diberikan di sini tidak benar-benar dalam C ++ atau agak terlalu rumit.

Harap dicatat bahwa dalam contoh saya createDirTree () sangat sederhana karena semua pengangkatan berat (pemeriksaan kesalahan, validasi jalur) perlu dilakukan oleh createDir (). Juga createDir () harus mengembalikan nilai true jika direktori sudah ada atau semuanya tidak berfungsi.

Inilah cara saya melakukannya di 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;
}

Tentu saja fungsi createDir () akan spesifik untuk sistem dan sudah ada cukup banyak contoh di jawaban lain bagaimana menulisnya untuk linux, jadi saya memutuskan untuk melewatkannya.

Tom
sumber
1

Jika dir tidak ada, buatlah:

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

Begitu banyak pendekatan telah dijelaskan di sini, tetapi kebanyakan dari mereka membutuhkan pengkodean keras jalur Anda ke dalam kode Anda. Ada solusi mudah untuk masalah itu, menggunakan QDir dan QFileInfo, dua kelas kerangka kerja Qt. Karena Anda sudah berada di lingkungan Linux, Qt harus mudah digunakan.

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

Pastikan Anda memiliki akses tulis ke Path itu.

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

touch /dir/to/the/file/thefile.ending
Tim Cooper
sumber
yang -ppilihan adalah apa yang saya cari. Terima kasih!
asgs
0

Berikut adalah fungsi rekursif C / C ++ yang digunakan dirname()untuk melintasi dari bawah ke atas pohon direktori. Ini akan berhenti begitu menemukan leluhur yang ada.

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

Yang lain memberi Anda jawaban yang benar, tetapi saya pikir saya akan mendemonstrasikan hal lain yang dapat Anda lakukan:

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

Akan membuat jalur berikut:

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

Tanda kurung memungkinkan Anda membuat beberapa direktori sekaligus pada tingkat hierarki yang sama, sedangkan -popsi berarti "buat direktori induk sesuai kebutuhan".

rmeador
sumber
setelah melihat jawaban Paul, saya menyadari bahwa saya (dan banyak orang lain) salah memahami pertanyaan ...
rmeador
Jika seseorang dapat memperbarui ini dengan mengubah ke sistem ("mkdir -p / tmp / a / {b, c} / d"), karena pertanyaannya bukan tentang melakukannya di shell .. tetapi melalui C ++.
Lipis
Saya pikir {a, b} akan bekerja di shell yang diturunkan sh dan csh. Saya tidak yakin apakah itu akan berfungsi dalam perintah system ().
Paul Tomblin
1
@Lipis: melakukan itu melalui system () bukanlah solusi yang baik untuk pertanyaan OP. @Andy: Saya tidak pernah mempertimbangkan itu sebelumnya, tetapi saya baru saja mengujinya dengan mengganti "mkdir -p" dengan "echo" dan mencetak "/ tmp / a / b / d / tmp / a / c / d", yang menyarankan shell yang melakukannya, bukan mkdir.
rmeador
@rmeador: jika ini bukan solusi yang baik, apakah Anda punya saran lain? Saya ingin melakukan itu melalui C ++ ... itu masalah saya bukan bagaimana melakukannya melalui shell ..
Lipis