如何在 Linux 上使用 C++ 创建目录树?

2024-10-10 08:38:00
admin
原创
73
摘要:问题描述:我想要一种在 Linux 上使用 C++ 创建多个目录的简单方法。例如我想在目录中保存一个文件 lola.file:/tmp/a/b/c 但如果目录不存在,我希望它们能够自动创建。一个可行的示例就完美了。解决方案 1:使用 Boost.Filesystem 很简单:create_directorie...

问题描述:

我想要一种在 Linux 上使用 C++ 创建多个目录的简单方法。

例如我想在目录中保存一个文件 lola.file:

/tmp/a/b/c

但如果目录不存在,我希望它们能够自动创建。一个可行的示例就完美了。


解决方案 1:

使用 Boost.Filesystem 很简单:create_directories

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

返回:true如果创建了新目录,否则返回false

解决方案 2:

<filesystem>在 C++17 或更高版本中,有带函数的
标准标头std::filesystem::create_directories
,可用于现代 C++ 程序。不过,C++ 标准函数没有 POSIX 特定的显式权限 (模式) 参数。

但是,这里有一个可以用 C++ 编译器进行编译的 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 = '';
            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
",
                            (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
", argv[i]);
    }
    return(0);
}

#endif /* TEST */

STRDUP()和是和的FREE()错误检查版本
,在中声明(并在和中实现
)。标头处理和的损坏版本
,可以在现代 Unix 系统上用替换(但在 1990 年存在许多问题)。和声明。strdup()`free()emalloc.hemalloc.cestrdup.c"sysstat.h"&lt;sys/stat.h><sys/stat.h>&quot;mkpath.h&quot;mkpath()`

v1.12(答案的原始版本)和 v1.13(答案的修订版本)之间的变化是EEXIST
的测试do_mkdir()。Switch 指出这是必要的

  • 谢谢, Switch。测试代码已经升级并在 MacBook Pro(2.3GHz Intel Core i7,运行 Mac OS X 10.7.4)上重现了问题,并表明问题已在修订版中得到修复(但测试只能显示错误的存在,而不能显示它们不存在)。显示的代码现在是 v1.16;自 v1.13 以来进行了外观或管理更改(例如仅在测试代码中使用mkpath.h而不是jlss.h和 无条件包含&lt;unistd.h>)。有理由认为&quot;sysstat.h&quot;应该用 代替,
    &lt;sys/stat.h>除非您的系统异常顽固。

(特此授权您可以将此代码用于任何目的并注明出处。)

该代码可在我的GitHub 上的SOQ
(Stack Overflow Questions)存储库中作为文件mkpath.c
mkpath.h(等)在
src/so-0067-5039
子目录中使用。

解决方案 3:

system(&quot;mkdir -p /tmp/a/b/c&quot;)

是我能想到的最短的方法(就代码长度而言,不一定是执行时间)。

它不是跨平台的,但可以在 Linux 下运行。

解决方案 4:

下面是我的代码示例(适用于 Windows 和 Linux):

#include &lt;iostream>
#include &lt;string>
#include &lt;sys/stat.h> // stat
#include &lt;errno.h>    // errno, ENOENT, EEXIST
#if defined(_WIN32)
#include &lt;direct.h>   // _mkdir
#endif

bool isDirExist(const std::string&amp; path)
{
#if defined(_WIN32)
    struct _stat info;
    if (_stat(path.c_str(), &amp;info) != 0)
    {
        return false;
    }
    return (info.st_mode &amp; _S_IFDIR) != 0;
#else 
    struct stat info;
    if (stat(path.c_str(), &amp;info) != 0)
    {
        return false;
    }
    return (info.st_mode &amp; S_IFDIR) != 0;
#endif
}

bool makePath(const std::string&amp; 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&#039;t exist, try to create it
        {
            int pos = path.find_last_of(&#039;/&#039;);
            if (pos == std::string::npos)
#if defined(_WIN32)
                pos = path.find_last_of(&#039;\&#039;);
            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&lt;argc; i++)
    {
        std::cout &lt;&lt; &quot;creating &quot; &lt;&lt; ARGV[i] &lt;&lt; &quot; ... &quot; &lt;&lt; (makePath(ARGV[i]) ? &quot;OK&quot; : &quot;failed&quot;) &lt;&lt; std::endl;
    }
    return 0;
}

用法:

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

解决方案 5:

需要注意的是,从 C++17 开始,文件系统接口是标准库的一部分。这意味着可以使用以下方法创建目录:

#include &lt;filesystem>

std::filesystem::create_directories(&quot;/a/b/c/d&quot;)

更多信息请访问:https: //en.cppreference.com/w/cpp/filesystem/create_directory

此外,使用 gcc 时,需要将“-std=c++17”添加到 CFLAGS。将“-lstdc++fs”添加到 LDLIBS。后者将来可能不再需要。

解决方案 6:

#include &lt;sys/types.h>
#include &lt;sys/stat.h>

int status;
...
status = mkdir(&quot;/tmp/a/b/c&quot;, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);

从这里开始。您可能必须对 /tmp、/tmp/a、/tmp/a/b/ 和 /tmp/a/b/c 分别执行 mkdirs,因为 C api 中没有与 -p 标志等效的标志。在执行上层操作时,请务必忽略 EEXISTS errno。

解决方案 7:

这与上一个类似,但向前遍历字符串而不是向后递归。为最后一次失败保留正确的 errno 值。如果有一个前导斜杠,则循环会多花费一些时间,这可以通过在循环外使用一个 find_first_of() 或通过检测前导 / 并将 pre 设置为 1 来避免。无论我们是通过第一次循环还是预循环调用进行设置,效率都是相同的,并且使用预循环调用时复杂性会(略)更高。

#include &lt;iostream>
#include &lt;string>
#include &lt;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]!=&#039;/&#039;){
        // force trailing / so we can handle everything in loop
        s+=&#039;/&#039;;
    }

    while((pos=s.find_first_of(&#039;/&#039;,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)) &amp;&amp; errno!=EEXIST){
            return mdret;
        }
    }
    return mdret;
}

int main()
{
    int mkdirretval;
    mkdirretval=mkpath(&quot;./foo/bar&quot;,0755);
    std::cout &lt;&lt; mkdirretval &lt;&lt; &#039;
&#039;;

}

解决方案 8:

你说的是“C++”,但这里的每个人似乎都在想“Bash shell”。

查看 gnu 的源代码mkdir;然后您就可以看到如何用 C++ 实现 shell 命令。

解决方案 9:

bool mkpath( std::string path )
{
    bool bSuccess = false;
    int nRC = ::mkdir( path.c_str(), 0775 );
    if( nRC == -1 )
    {
        switch( errno )
        {
            case ENOENT:
                //parent didn&#039;t exist, try to create it
                if( mkpath( path.substr(0, path.find_last_of(&#039;/&#039;)) ) )
                    //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;
}

解决方案 10:

所以我今天需要mkdirp(),并且发现此页面上的解决方案过于复杂。因此我写了一个相当短的片段,可以轻松复制到偶然发现此主题并想知道为什么我们需要这么多行代码的人中。

mkdirp文件

#ifndef MKDIRP_H
#define MKDIRP_H

#include &lt;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 &lt;errno.h>

bool mkdirp(const char* path, mode_t mode) {
  // const cast for hack
  char* p = const_cast&lt;char*>(path);

  // Do mkdir for each slash until end of string or error
  while (*p != &#039;&#039;) {
    // Skip first character
    p++;

    // Find first slash or end
    while(*p != &#039;&#039; &amp;&amp; *p != &#039;/&#039;) p++;

    // Remember value from p
    char v = *p;

    // Write end of string at p
    *p = &#039;&#039;;

    // Create folder from path to &#039;&#039; inserted at p
    if(mkdir(path, mode) == -1 &amp;&amp; errno != EEXIST) {
      *p = v;
      return false;
    }

    // Restore path to it&#039;s former glory
    *p = v;
  }

  return true;
}

如果您不喜欢 const 转换和临时修改字符串,只需在之后执行strdup()and free()it 即可。

解决方案 11:

由于这篇文章在 Google 上“创建目录树”的排名很高,我将发布一个适用于 Windows 的答案 — 这将使用为 UNICODE 或 MBCS 编译的 Win32 API 来工作。这是从上述 Mark 的代码移植而来的。

由于我们使用的是 Windows,因此目录分隔符是反斜杠,而不是正斜杠。如果您希望使用正斜杠,请更改&#039;&#039;&#039;/&#039;

它将适用于:

c:ooarhelloworld

c:ooarhellpworld\n

(即:不需要尾随斜杠,因此您不必检查它。)

在说“只需在 Windows 中使用SHCreateDirectoryEx() ”之前,请注意SHCreateDirectoryEx()已被弃用,并且可能会随时从 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,&#039;\&#039;);
                _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;
}

解决方案 12:

我知道这是一个老问题,但它在谷歌搜索结果中排名很高,而且这里提供的答案实际上不是 C++ 的,或者有点太复杂。

请注意,在我的示例中,createDirTree() 非常简单,因为所有繁重的工作(错误检查、路径验证)无论如何都需要由 createDir() 完成。此外,如果目录已存在,createDir() 应该返回 true,否则整个过程将无法正常工作。

以下是我在 C++ 中实现的方法:

#include &lt;iostream>
#include &lt;string>

bool createDir(const std::string dir)
{
    std::cout &lt;&lt; &quot;Make sure dir is a valid path, it does not exist and create it: &quot;
              &lt;&lt; dir &lt;&lt; std::endl;
    return true;
}

bool createDirTree(const std::string full_path)
{
    size_t pos = 0;
    bool ret_val = true;

    while(ret_val == true &amp;&amp; pos != std::string::npos)
    {
        pos = full_path.find(&#039;/&#039;, pos + 1);
        ret_val = createDir(full_path.substr(0, pos));
    }

    return ret_val;
}

int main()
{
    createDirTree(&quot;/tmp/a/b/c&quot;);
    return 0;
}

当然,createDir() 函数是系统特定的,并且其他答案中已经有足够多关于如何为 Linux 编写它的示例,所以我决定跳过它。

解决方案 13:

这里描述了许多方法,但大多数方法都需要将路径硬编码到代码中。有一个简单的解决方案,使用 QT 框架的两个类 QDir 和 QFileInfo。由于您已经在 Linux 环境中,因此使用 Qt 应该很容易。

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

确保您对该路径具有写权限。

解决方案 14:

如果目录不存在,则创建它:

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

解决方案 15:

这是 C/C++ 递归函数,用于dirname()自下而上遍历目录树。一旦找到现有祖先,它就会停止。

#include &lt;libgen.h>
#include &lt;string.h>

int create_dir_tree_recursive(const char *path, const mode_t mode)
{
    if (strcmp(path, &quot;/&quot;) == 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, &amp;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;
}

解决方案 16:

如果您还没有 C++17 并且正在寻找与平台无关的解决方案,请使用ghc::filesystem。仅包含标头的代码与 C++17 兼容(实际上是反向移植),并且以后易于迁移。

解决方案 17:

mkdir -p /dir/to/the/file

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

解决方案 18:

其他人都给了你正确答案,但我想我会向你展示另一件你可以做的巧妙的事情:

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

将创建以下路径:

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

括号允许您在层次结构的同一级别上一次创建多个目录,而选项-p表示“根据需要创建父目录”。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用