Làm cách nào để tạo cây thư mục trong C ++ / Linux?


109

Tôi muốn một cách dễ dàng để tạo nhiều thư mục trong C ++ / Linux.

Ví dụ, tôi muốn lưu một tệp lola.file trong thư mục:

/tmp/a/b/c

nhưng nếu các thư mục không có ở đó, tôi muốn chúng được tạo tự động. Một ví dụ làm việc sẽ là hoàn hảo.


C ++ không có bất kỳ tích tại các cơ sở cho việc tạo ra các thư mục và cây cho mỗi gia nhập . Bạn sẽ phải sử dụng C và các cuộc gọi hệ thống hoặc một thư viện bên ngoài như Boost. C và các cuộc gọi hệ thống sẽ phụ thuộc vào nền tảng.
jww

6
@noloader Cảm ơn một người đàn ông rất nhiều .. nhưng tôi nghĩ rằng sau 4 năm, tôi khá nhiều có câu trả lời của tôi là bạn có thể nhìn thấy dưới đây trong 13 cách khác nhau ...
Lipis

Vâng, tôi rất ngạc nhiên khi không ai nói rõ ràng rằng bạn không thể làm điều đó trong C ++ (giả sử bạn muốn một phương thức di động trong C ++ hoạt động trên Linux). Nhưng có lẽ bạn đã biết điều đó;). Tuy nhiên, có rất nhiều gợi ý hay cho mã C không di động.
jww


3
@LightnessRacesinOrbit Đó là năm đại học của tôi trong C ++ trên Linux :)
Lipis

Câu trả lời:


59

Với C ++ 17 trở lên, có tiêu đề tiêu chuẩn <filesystem>với hàm std::filesystem::create_directories nên được sử dụng trong các chương trình C ++ hiện đại. Mặc dù vậy, các hàm tiêu chuẩn C ++ không có đối số quyền (chế độ) rõ ràng dành riêng cho POSIX.

Tuy nhiên, đây là một hàm C có thể được biên dịch bằng trình biên dịch 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 */

Các macro STRDUP()FREE()là phiên bản kiểm tra lỗi của strdup()free(), được khai báo trong emalloc.h(và được triển khai trong emalloc.cestrdup.c). Các "sysstat.h"giao dịch tiêu đề với các phiên bản chia của <sys/stat.h> và có thể được thay thế bằng <sys/stat.h>trên các hệ thống Unix hiện đại (nhưng có rất nhiều vấn đề trở lại vào năm 1990). Và "mkpath.h"tuyên bố mkpath().

Sự thay đổi giữa v1.12 (phiên bản gốc của câu trả lời) và v1.13 (phiên bản sửa đổi của câu trả lời) là thử nghiệm cho EEXISTtrong do_mkdir(). Điều này đã được Switch chỉ ra là cần thiết - cảm ơn bạn, Switch. Mã kiểm tra đã được nâng cấp và tái tạo sự cố trên MacBook Pro (Intel Core i7 2.3GHz, chạy Mac OS X 10.7.4) và cho thấy rằng sự cố đã được khắc phục trong bản sửa đổi (nhưng thử nghiệm chỉ có thể cho thấy sự hiện diện của lỗi , không bao giờ vắng mặt của họ). Mã hiển thị bây giờ là v1.16; đã có những thay đổi về mỹ phẩm hoặc hành chính được thực hiện kể từ v1.13 (chẳng hạn như sử dụng mkpath.hthay vì jlss.hvà chỉ đưa <unistd.h>vào mã thử nghiệm một cách vô điều kiện). Thật hợp lý khi lập luận rằng "sysstat.h"nên được thay thế bằng <sys/stat.h>trừ khi bạn có một hệ thống ngoan cố bất thường.

(Theo đây, bạn được phép sử dụng mã này cho bất kỳ mục đích nào có ghi công.)

Mã này có sẵn trong kho lưu trữ SOQ (Câu hỏi dồn về ngăn xếp) của tôi trên GitHub dưới dạng tệp mkpath.cmkpath.h(v.v.) trong thư mục con src / so-0067-5039 .


2
Nó chắc chắn là nhanh hơn hệ thống. Hệ thống có rất nhiều chi phí liên quan. Về cơ bản, quá trình này phải được chia hai, sau đó ít nhất hai tập tin nhị phân phải được nạp (một lẽ sẽ nằm trong bộ nhớ cache đã được), trên trong số đó sẽ được thêm một ngã ba của người kia, ...
ypnos

1
Tôi quên: Và sau đó "mkdir -p" sẽ làm ít nhất giống như mã được đăng ở trên!
ypnos

7
Có một điều kiện chủng tộc tinh vi trong đoạn mã này mà tôi thực sự gặp phải. Nó chỉ xảy ra khi nhiều chương trình khởi động đồng thời và tạo đường dẫn thư mục giống nhau. Cách khắc phục là thêm if (errno != EEXIST) { status = -1; }khi mkdir bị lỗi.
Chuyển

2
@Switch: Cảm ơn. Đó là vấn đề với việc sử dụng stat()trước đây mkdir(); nó là một vấn đề TOCTOU (thời gian kiểm tra, thời gian sử dụng). Tôi đã thử kiểm tra lỗi bằng một tập lệnh shell chạy 13 quy trình trong nền tạo ra cùng một đường dẫn 29 phần tử và không tìm cách khắc phục nó. Sau đó, tôi đã hack chương trình thử nghiệm đến 20 lần và yêu cầu mỗi đứa trẻ thử, và điều đó đã giải quyết được lỗi. Mã cố định sẽ có if (mkdir(path, mode) != 0 && errno != EEXIST) status = -1;. Điều đó không hiển thị lỗi.
Jonathan Leffler

2
@DavidMerinos: chúng là tiêu đề ( jlss.h, emalloc.h), không phải thư viện. Tuy nhiên, mã có sẵn trong tôi SOQ (Câu hỏi Stack Overflow) kho trên GitHub như các file jlss.h, emalloc.cemalloc.htrong src / libsoq thư mục con. Bạn sẽ cần phải posixver.hquá, và một vài người khác ( debug.h, stderr.c, stderr.h- Tôi nghĩ đó là nó, nhưng những gì bạn cần tất cả phải ở trong thư mục đó).
Jonathan Leffler

157

Dễ dàng với Boost. create_directories

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

Trả về: truenếu một thư mục mới được tạo, ngược lại false.


9
Chà, hầu hết các thư viện tăng cường chỉ có tiêu đề, có nghĩa là không có chi phí nào ngoài những gì bạn sử dụng. Trong trường hợp của Boost.Filesystem, nó yêu cầu biên dịch. Trên đĩa của tôi, thư viện đã biên dịch nặng ~ 60KB.
Benoît

1
@Lipis: vui lòng chính xác hệ thống nhúng của bạn là gì. Tôi tin rằng nó sẽ có sẵn trên hầu hết mọi bản phân phối linux.
Benoît

4
Về trên C ++ 11 trình biên dịch được đề cập bởi @danijar, bình luận ở đây đã làm cho nó rõ ràng hơn: 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 không chỉ có tiêu đề: stackoverflow.com/questions/13604090/…
ftvs

2
IMHO: Trong bất kỳ dự án nào của tôi nhằm làm một điều gì đó quan trọng và đứng vững trước thử thách của thời gian, thật đáng để biên dịch để có một bộ công cụ chuẩn hóa cực kỳ hữu ích và mạnh mẽ như vậy. Rất nhiều trong số đó đã được đưa vào C ++ tiêu chuẩn, chắc chắn sẽ còn nhiều hơn thế nữa. Hãy thử nó, gắn bó với nó, bạn sẽ có lợi nếu bạn có nhiều hơn những nhu cầu tầm thường và bạn không muốn phát minh lại bánh xe. :-)
moodboom

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

là cách ngắn nhất mà tôi có thể nghĩ ra (về độ dài của mã, không nhất thiết là thời gian thực thi).

Nó không đa nền tảng nhưng sẽ hoạt động trên Linux.


1
NẾU bạn sẽ cung cấp cho các giải pháp như một lệnh shell, nó sẽ là tốt để đề cập đến hệ thống (3)
dmckee --- cựu điều hành 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);

Từ đây . Bạn có thể phải thực hiện các mkdirs riêng biệt cho / tmp, / tmp / a, / tmp / a / b / và sau đó là / tmp / a / b / c vì không có cờ -p tương đương trong C api. Hãy chắc chắn và bỏ qua lỗi EEXISTS trong khi bạn đang thực hiện các bước cấp cao hơn.


Thực tế thú vị: Ít nhất Solaris và HP / UX có mkdirp (), mặc dù nó rõ ràng không tối ưu cho tính di động.
Martin Carpenter

đó là điểm .. mà tôi không muốn gọi tất cả các hàm này một cách riêng biệt.
Lipis

Gọi mkdir một vài lần sẽ nhanh hơn cách gọi hệ thống một lần.
Paul Tomblin

Tôi không hiểu bạn đang đề nghị gì: gọi mkdir 4 lần với các đối số khác nhau? ("/tmp/",...), ("/tmp/a/",...), ("/tmp/a/b/",...),("/tmp/a/b/c/",...)
Antonio

1
Một lần nữa, việc thực hiện cùng một cuộc gọi ba lần là điều khá đơn giản. Mục đích là cung cấp cho mọi người đủ thông tin để họ có thể viết mã, chứ không phải viết mã cho họ.
Paul Tomblin

25

Đây là ví dụ của tôi về mã (nó hoạt động cho cả Windows và 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;
}

Sử dụng:

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

Tôi thứ hai nhận xét đó! (Đáng ngạc nhiên) không phải là một nhiệm vụ tầm thường khi tìm một cách C ++ di động để tạo một thư mục. Câu trả lời này cần nhiều lượt ủng hộ hơn.
Manuel Lafond

1
Trong Windows, isDirExist không hoạt động nếu ký tự ở cuối là dấu gạch chéo ngược. Luôn trả về false. Tôi phải sửa đổi mã thành: std :: string dirPath (path); while ('\\' == * dirPath.rbegin ()) dirPath.pop_back (); ... và sau đó, tất nhiên, chuyển dirPath.c_str () trong lệnh gọi tới _stat.
MiloDC

API Windows có "Tên không phải ANSI để tương thích" để stat(liên quan đến __STDC__) không cần kiểm tra trình biên dịch trước.
Sandburg

18

Cần lưu ý rằng bắt đầu từ C ++ 17 giao diện hệ thống tệp là một phần của thư viện chuẩn. Điều này có nghĩa là người ta có thể có những điều sau để tạo thư mục:

#include <filesystem>

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

Thông tin thêm tại đây: https://en.cppreference.com/w/cpp/filesystem/create_directory

Ngoài ra, với gcc, người ta cần "-std = c ++ 17" thành CFLAGS. Và "-lstdc ++ fs" thành LDLIBS. Khả năng sau này sẽ không được yêu cầu trong tương lai.


Cũng nên làm việc với Visual C ++ đủ mới và "/ std: c ++ mới nhất". Xem: blog.msdn.microsoft.com/vcblog/2018/05/07/…developercommunity.visualstudio.com/content/problem/296680/…
Ron Burk vào

9

Điều này tương tự như trước đó nhưng hoạt động chuyển tiếp thông qua chuỗi thay vì ngược lại một cách đệ quy. Để lại sai sót với giá trị phù hợp cho lần thất bại cuối cùng. Nếu có dấu gạch chéo ở đầu, sẽ có thêm thời gian thông qua vòng lặp mà có thể tránh được thông qua một find_first_of () bên ngoài vòng lặp hoặc bằng cách phát hiện dấu gạch chéo đầu / và đặt trước 1. Hiệu quả là như nhau cho dù chúng ta có thiết lập vòng lặp đầu tiên hoặc một cuộc gọi vòng lặp trước và độ phức tạp sẽ cao hơn (một chút) khi sử dụng cuộc gọi vòng lặp trước.

#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

Bạn đã nói "C ++" nhưng mọi người ở đây dường như đang nghĩ "Bash shell."

Kiểm tra mã nguồn để gnu mkdir; thì bạn có thể xem cách triển khai các lệnh shell trong C ++.


Hệ thống tốt ("mkdir ...") nên thực hiện thủ thuật trên linux. Nó không phải là đa nền tảng.
ChristopheD

Tôi thứ hai những gì @MartinCarpenter nói
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;
}

đó là giải pháp tốt nhất cho tôi! Cảm ơn bạn!)))
neo

có thể đó là một câu hỏi kết xuất, nhưng tiền tố "::" trước mkdir là gì?
Rayee Roded

1
Đã tìm thấy câu trả lời, :: đảm bảo rằng độ phân giải xảy ra từ không gian tên toàn cầu stackoverflow.com/questions/4269034/…
Rayee Roded

4

Vì vậy, tôi cần mkdirp()ngày hôm nay và nhận thấy các giải pháp trên trang này quá phức tạp. Do đó, tôi đã viết một đoạn mã khá ngắn, có thể dễ dàng sao chép vào cho những người khác tình cờ gặp chủ đề này và tự hỏi tại sao chúng ta cần nhiều dòng mã như vậy.

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

Nếu bạn không thích ép kiểu const và tạm thời sửa đổi chuỗi, chỉ cần thực hiện một strdup()free()sau đó.


Được đăng lên ý chính, vì vậy tôi không quên nơi tôi đặt nó vào lần sau khi cần nó :) gist.github.com/jonasfj/7797272
jonasfj

2
Thật là ác khi cố gắng sửa đổi một chuỗi được truyền dưới dạng một hằng số. Tất cả những điều khác, nó có khả năng dẫn đến thất bại nghiêm trọng nếu nó từng được chuyển qua một chuỗi ký tự.
Jonathan Leffler

2
Hoàn toàn đúng .... đây là thực sự xấu ... có lẽ strcpy sẽ làm điều này tốt hơn ...
jonasfj

3

Vì bài đăng này được xếp hạng cao trong Google cho "Tạo cây thư mục", tôi sẽ đăng một câu trả lời sẽ hoạt động cho Windows - điều này sẽ hoạt động bằng cách sử dụng Win32 API được biên dịch cho UNICODE hoặc MBCS. Điều này được chuyển từ mã của Mark ở trên.

Vì đây là Windows mà chúng tôi đang làm việc, các dấu phân cách thư mục là dấu gạch chéo LẠI, không phải dấu gạch chéo về phía trước. Nếu bạn muốn có dấu gạch chéo về phía trước, hãy thay đổi '\\'thành'/'

Nó sẽ hoạt động với:

c:\foo\bar\hello\world

c:\foo\bar\hellp\world\

(nghĩa là: không cần dấu gạch chéo, vì vậy bạn không cần phải kiểm tra nó.)

Trước khi nói "Chỉ sử dụng SHCreateDirectoryEx () trong Windows", hãy lưu ý rằng SHCreateDirectoryEx () không được dùng nữa và có thể bị xóa bất kỳ lúc nào khỏi các phiên bản Windows trong tương lai.

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

Một chút sửa đổi - nếu đường dẫn chứa dấu gạch chéo ngược, điều này không hoạt động. Ở đây: `LPCTSTR szLast = _tcsrchr (szPathTree, '\\');` Bạn chỉ cần thêm cái này: `` `if (nullptr == szLast) {szLast = _tcsrchr (szPathTree, '/'); } `` '
Den-Jason

1
Cảm ơn bạn về thông tin. Điều gì xảy ra nếu một đường dẫn được trộn lẫn? tức là: c:\this\is\a/mixed/path\of\slashesThông thường dấu gạch chéo của Windows là dấu gạch chéo ngược. Điều sẽ xảy ra là người gọi nên làm sạch đường dẫn và đảm bảo tất cả các dấu gạch chéo đều phù hợp trước khi gọi phương thức này.
Andy

3

Tôi biết đó là một câu hỏi cũ nhưng nó hiển thị cao trên kết quả tìm kiếm của google và các câu trả lời được cung cấp ở đây không thực sự bằng C ++ hoặc hơi quá phức tạp.

Xin lưu ý rằng trong ví dụ của tôi, createDirTree () rất đơn giản vì tất cả các công việc nặng nhọc (kiểm tra lỗi, xác thực đường dẫn) cần phải được thực hiện bởi createDir (). Ngoài ra createDir () nên trả về true nếu thư mục đã tồn tại hoặc toàn bộ điều đó sẽ không hoạt động.

Đây là cách tôi sẽ làm điều đó trong 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;
}

Tất nhiên hàm createDir () sẽ dành riêng cho hệ thống và đã có đủ ví dụ trong các câu trả lời khác về cách viết nó cho linux, vì vậy tôi quyết định bỏ qua nó.


1

Nếu dir không tồn tại, hãy tạo nó:

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

1

Vì vậy, nhiều cách tiếp cận đã được mô tả ở đây nhưng hầu hết chúng đều cần mã hóa cứng đường dẫn vào mã của bạn. Có một giải pháp dễ dàng cho vấn đề đó, sử dụng QDir và QFileInfo, hai lớp của Qt framework. Vì bạn đã ở trong môi trường Linux, nên dễ dàng sử dụng Qt.

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

Đảm bảo rằng bạn có quyền ghi vào Đường dẫn đó.


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

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

các -plựa chọn là những gì tôi đang tìm kiếm. Cảm ơn!
asgs

0

Đây là hàm đệ quy C / C ++ sử dụng dirname()để duyệt từ dưới lên của cây thư mục. Nó sẽ dừng ngay khi tìm thấy tổ tiên hiện có.

#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

Những người khác đã cho bạn câu trả lời đúng, nhưng tôi nghĩ tôi sẽ chứng minh một điều gọn gàng khác mà bạn có thể làm:

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

Sẽ tạo các đường dẫn sau:

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

Dấu ngoặc nhọn cho phép bạn tạo nhiều thư mục cùng một lúc trên cùng một cấp của hệ thống phân cấp, trong khi -ptùy chọn có nghĩa là "tạo thư mục mẹ khi cần".


sau khi nhìn thấy câu trả lời của Thánh Phaolô, tôi nhận ra rằng tôi (và nhiều người khác) hiểu lầm câu hỏi ...
rmeador

Nếu ai đó có thể Chỉ cần cập nhật điều này bằng cách thay đổi thành hệ thống ("mkdir -p / tmp / a / {b, c} / d"), vì câu hỏi không phải là về việc thực hiện nó trong shell .. mà là thông qua C ++.
Lipis

Tôi nghĩ rằng {a, b} sẽ hoạt động trong cả shell dẫn xuất sh và dẫn xuất csh. Tuy nhiên, tôi không chắc liệu nó có hoạt động trong lệnh system () hay không.
Paul Tomblin

1
@Lipis: làm điều đó thông qua system () không phải là một giải pháp tốt cho câu hỏi của OP. @Andy: Tôi chưa bao giờ xem xét điều đó trước đây, nhưng tôi vừa thử nghiệm nó bằng cách thay thế "mkdir -p" bằng "echo" và nó in ra "/ tmp / a / b / d / tmp / a / c / d", điều này cho thấy đó là shell làm việc đó, không phải mkdir.
rmeador

@rmeador: nếu nó không phải là một giải pháp tốt, bạn có điều gì khác để đề xuất không? Tôi muốn làm điều đó thông qua C ++ ... đó là vấn đề của tôi không phải là làm thế nào để làm điều đó thông qua shell ..
Lipis
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.