Unix上的递归mkdir()系统调用

Ale*_*all 65 c unix posix mkdir

在阅读具有该名称的Unix系统调用的mkdir(2)手册页后,看来该调用不会在路径中创建中间目录,只会在路径中创建最后一个目录.是否有任何方法(或其他功能)来创建路径中的所有目录,而无需手动解析我的目录字符串并单独创建每个目录?

Car*_*rum 86

不幸的是,没有系统调用来为你做这件事.我猜这是因为没有办法为错误情况下应该发生的事情提供真正明确定义的语义.它应该离开已经创建的目录吗?删除它们?如果删除失败怎么办?等等...

然而,滚动你自己很容易,快速谷歌的' 递归mkdir '提出了许多解决方案.这是一个接近顶部的:

http://nion.modprobe.de/blog/archives/357-Recursive-directory-creation.html

static void _mkdir(const char *dir) {
        char tmp[256];
        char *p = NULL;
        size_t len;

        snprintf(tmp, sizeof(tmp),"%s",dir);
        len = strlen(tmp);
        if(tmp[len - 1] == '/')
                tmp[len - 1] = 0;
        for(p = tmp + 1; *p; p++)
                if(*p == '/') {
                        *p = 0;
                        mkdir(tmp, S_IRWXU);
                        *p = '/';
                }
        mkdir(tmp, S_IRWXU);
}
Run Code Online (Sandbox Code Playgroud)

  • 改进版本:https://gist.github.com/JonathonReinhart/8c0d90191c38af2dcadb102c4e202950 (9认同)
  • 我唯一要改变的是tmp [256]到tmp [PATH_MAX]也是#include <limits.h> (7认同)

j03*_*03m 66

嗯,我以为mkdir -p会那么做?

mkdir -p this /是/ a/full/path/of/stuff

  • 是的确如此,但问题与C函数调用有关. (48认同)
  • 事实上 - 赞成票可能反映出这对许多人来说是一个有用的答案,**但它是一个不同于问题**的问题的答案. (9认同)
  • 然而,人们可以查看mkdir的源代码,看看它是如何做到的.做一个快速的谷歌,似乎相关的代码是在[coreutils]的[mkancestdirs.c](https://fossies.org/dox/coreutils-8.23/mkancesdirs_8c_source.html) (4认同)

Yar*_*hiy 23

这是我的解决方案.通过调用下面的函数,您可以确保存在指向文件路径的所有dirs.请注意,file_path参数不是此处的目录名,而是调用后要创建的文件的路径mkpath().

例如,如果不存在mkpath("/home/me/dir/subdir/file.dat", 0755)则应创建/home/me/dir/subdir.mkpath("/home/me/dir/subdir/", 0755)做同样的事.

也适用于相对路径.

发生错误时返回-1并设置errno.

int mkpath(char* file_path, mode_t mode) {
    assert(file_path && *file_path);
    for (char* p = strchr(file_path + 1, '/'); p; p = strchr(p + 1, '/')) {
        *p = '\0';
        if (mkdir(file_path, mode) == -1) {
            if (errno != EEXIST) {
                *p = '/';
                return -1;
            }
        }
        *p = '/';
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

请注意,file_path在操作期间会对其进行修改,但之后会进行恢复.因此file_path并不严格const.


tro*_*bit 11

这是另一种mkpath()使用递归的方法,它既小又可读.它strdupa()用于避免dir直接改变给定的字符串参数并避免使用malloc()&free().确保编译-D_GNU_SOURCE以激活strdupa()...意味着此代码仅适用于GLIBC,EGLIBC,uClibc和其他GLIBC兼容的C库.

int mkpath(char *dir, mode_t mode)
{
    if (!dir) {
        errno = EINVAL;
        return 1;
    }

    if (strlen(dir) == 1 && dir[0] == '/')
        return 0;

    mkpath(dirname(strdupa(dir)), mode);

    return mkdir(dir, mode);
}
Run Code Online (Sandbox Code Playgroud)

在这里和Valery Frolov输入之后,在Inadyn项目中,以下修订版本mkpath()现已被推到libite

int mkpath(char *dir, mode_t mode)
{
    struct stat sb;

    if (!dir) {
        errno = EINVAL;
        return 1;
    }

    if (!stat(dir, &sb))
        return 0;

    mkpath(dirname(strdupa(dir)), mode);

    return mkdir(dir, mode);
}
Run Code Online (Sandbox Code Playgroud)

它再使用一个系统调用,但是现在代码更易读.


Chi*_*chi 9

看一下这里的bash源代码,具体看看examples/loadables/mkdir.c,特别是136-210行.如果你不想这样做,这里有一些处理这个问题的来源(直接来自我链接的tar.gz):

/* Make all the directories leading up to PATH, then create PATH.  Note that
   this changes the process's umask; make sure that all paths leading to a
   return reset it to ORIGINAL_UMASK */

static int
make_path (path, nmode, parent_mode)
     char *path;
     int nmode, parent_mode;
{
  int oumask;
  struct stat sb;
  char *p, *npath;

  if (stat (path, &sb) == 0)
  {
      if (S_ISDIR (sb.st_mode) == 0)
      {
          builtin_error ("`%s': file exists but is not a directory", path);
          return 1;
      }

      if (chmod (path, nmode))
      {
          builtin_error ("%s: %s", path, strerror (errno));
          return 1;
      }

      return 0;
  }

  oumask = umask (0);
  npath = savestring (path);    /* So we can write to it. */

  /* Check whether or not we need to do anything with intermediate dirs. */

  /* Skip leading slashes. */
  p = npath;
  while (*p == '/')
    p++;

  while (p = strchr (p, '/'))
  {
      *p = '\0';
      if (stat (npath, &sb) != 0)
      {
          if (mkdir (npath, parent_mode))
          {
              builtin_error ("cannot create directory `%s': %s", npath, strerror (errno));
              umask (original_umask);
              free (npath);
              return 1;
          }
      }
      else if (S_ISDIR (sb.st_mode) == 0)
      {
          builtin_error ("`%s': file exists but is not a directory", npath);
          umask (original_umask);
          free (npath);
          return 1;
      }

      *p++ = '/';   /* restore slash */
      while (*p == '/')
          p++;
  }

  /* Create the final directory component. */
  if (stat (npath, &sb) && mkdir (npath, nmode))
  {
      builtin_error ("cannot create directory `%s': %s", npath, strerror (errno));
      umask (original_umask);
      free (npath);
      return 1;
  }

  umask (original_umask);
  free (npath);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

您可以通过不太常规的实现来逃避.


Sie*_*geX 7

显然不是,我的两个建议是:

char dirpath[80] = "/path/to/some/directory";
sprintf(mkcmd, "mkdir -p %s", dirpath);
system(mkcmd);
Run Code Online (Sandbox Code Playgroud)

或者,如果您不想使用system()try查看coreutils mkdir源代码并查看它们如何实现该-p选项.

  • OMG - 它是2014年,文件路径现在通常有空格.请不要这样编码 (9认同)
  • 糟糕的缓冲区溢出.你最好希望没有人创造一条以rm -rf~结尾的路径 (6认同)
  • @Lothar也许你没有意识到这个答案是在4年多前做出的,所以`OMG--它是2010年'会更贴切.也许只是我,但是在'%s'周围缺少引号似乎不适合将神灵带入混合体中.如果您想建议编辑,请随时这样做. (3认同)
  • / bin/sh将使用PATH环境变量来定位mkdir.如果名为mkdir的可执行文件位于/ bin之前的路径位置,则会执行该操作. (2认同)