首页 文章

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

提问于
浏览
59

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

14 回答

  • 1

    挺直的 . 这可能是一个很好的起点

    int makeDir(char *fullpath, mode_t permissions){
    int i=0;
    char *arrDirs[20];
    char aggrpaz[255];
    arrDirs[i] = strtok(fullpath,"/");
    strcpy(aggrpaz, "/");
    while(arrDirs[i]!=NULL)
    {
        arrDirs[++i] = strtok(NULL,"/");
        strcat(aggrpaz, arrDirs[i-1]);
        mkdir(aggrpaz,permissions);
        strcat(aggrpaz, "/");
    }
    i=0;
    return 0;
    }
    

    您解析此函数的完整路径加上您想要的权限,即 S_IRUSR ,有关完整的模式列表,请转到此处https://techoverflow.net/2013/04/05/how-to-use-mkdir-from-sysstat-h/

    完整路径字符串将由"/"字符拆分,并且各个目录将一次附加到 aggrpaz 字符串 . 每次循环迭代都会调用mkdir函数,并将其传递给目前为止的聚合路径以及权限 . 这个例子可以改进,我没有检查mkdir函数输出,这个函数只适用于绝对路径 .

  • 3

    我的解决方案

    int mkrdir(const char *path, int index, int permission)
    {
        char bf[NAME_MAX];
        if(*path == '/')
            index++;
        char *p = strchr(path + index, '/');
        int len;
        if(p) {
            len = MIN(p-path, sizeof(bf)-1);
            strncpy(bf, path, len);
            bf[len]=0;
        } else {
            len = MIN(strlen(path)+1, sizeof(bf)-1);
            strncpy(bf, path, len);
            bf[len]=0;
        }
    
        if(access(bf, 0)!=0) {
            mkdir(bf, permission);
            if(access(bf, 0)!=0) {
                return -1;
            }
        }
        if(p) {
            return mkrdir(path, p-path+1, permission);
        }
        return 0;
    }
    
  • 77

    我这样做的递归方式:

    #include <libgen.h> /* Only POSIX version of dirname() */
    #include <stdlib.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    static void recursive_mkdir(const char *path, mode_t mode)
    {
        char *spath = NULL;
        const char *next_dir = NULL;
    
        /* dirname() modifies input! */
        spath = strdup(path);
        if (spath == NULL)
        {
            /* Report error, no memory left for string duplicate. */
            goto done;
        }
    
        /* Get next path component: */
        next_dir = dirname(spath);
    
        if (access(path, F_OK) == 0)
        {
            /* The directory in question already exists! */
            goto done;
        }
    
        if (strcmp(next_dir, ".") == 0 || strcmp(next_dir, "/") == 0)
        {
            /* We reached the end of recursion! */
            goto done;
        }
    
        recursive_mkdir(next_dir, mode);
        if (mkdir(path, mode) != 0)
        {
           /* Report error on creating directory */
        }
    
    done:
        free(spath);
        return;
    }
    

    编辑:修复我的旧代码片段,错误报告Namchester

  • 11

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

    然而,很容易推出自己的,并快速谷歌为'recursive mkdir ' turned up a number of solutions. Here'的一个接近顶部:

    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);
    }
    
  • 0

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

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

    或者,如果您不想使用 system() ,请尝试查看coreutils mkdir 源代码,看看他们如何实现 -p 选项 .

  • 8

    这是对 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);
    }
    

    在这里和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);
    }
    

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

  • 1

    以下是我对更通用解决方案的看法:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    
    typedef int (*dirhandler_t)( const char*, void* );
    /// calls itfunc for each directory in path (except for . and ..)
    int iterate_path( const char* path, dirhandler_t itfunc, void* udata )
    {
        int rv = 0;
        char tmp[ 256 ];
        char *p = tmp;
        char *lp = tmp;
        size_t len;
        size_t sublen;
        int ignore_entry;
    
        strncpy( tmp, path, 255 );
    
        tmp[ 255 ] = '\0';
        len = strlen( tmp );
    
        if( 0 == len ||
            (1 == len && '/' == tmp[ 0 ]) )
            return 0;
    
        if( tmp[ len - 1 ] == '/' )
            tmp[ len - 1 ] = 0;
    
        while( (p = strchr( p, '/' )) != NULL )
        {
            ignore_entry = 0;
            *p = '\0';
            lp = strrchr( tmp, '/' );
    
            if( NULL == lp ) { lp = tmp; }
            else { lp++; }
    
            sublen = strlen( lp );
    
            if( 0 == sublen )   /* ignore things like '//' */
                ignore_entry = 1;
            else if( 1 == sublen &&  /* ignore things like '/./' */
                     '.' == lp[ 0 ] )
                ignore_entry = 1;
            else if( 2 == sublen &&    /* also ignore things like '/../' */
                     '.' == lp[ 0 ] &&
                     '.' == lp[ 1 ] )
                ignore_entry = 1;
    
            if( ! ignore_entry )
            {
                if( (rv = itfunc( tmp, udata )) != 0 )
                    return rv;
            }
    
            *p = '/';
            p++;
            lp = p;
        }
    
        if( strcmp( lp, "." ) && strcmp( lp, ".." ) )
            return itfunc( tmp, udata );
    
        return 0;
    }
    
    mode_t get_file_mode( const char* path )
    {
        struct stat statbuf;
        memset( &statbuf, 0, sizeof( statbuf ) );
    
        if( NULL == path ) { return 0; }
    
        if( 0 != stat( path, &statbuf ) )
        {
            fprintf( stderr, "failed to stat '%s': %s\n",
                     path, strerror( errno ) );
            return 0;
        }
    
        return statbuf.st_mode;
    }
    
    static int mymkdir( const char* path, void* udata )
    {
        (void)udata;
        int rv = mkdir( path, S_IRWXU );
        int errnum = errno;
    
        if( 0 != rv )
        {
            if( EEXIST == errno &&
                S_ISDIR( get_file_mode( path ) ) )  /* it's all good, the directory already exists */
                return 0;
    
            fprintf( stderr, "mkdir( %s ) failed: %s\n",
                     path, strerror( errnum ) );
        }
    //     else
    //     {
    //         fprintf( stderr, "created directory: %s\n", path );
    //     }
    
        return rv;
    }
    
    int mkdir_with_leading( const char* path )
    {
        return iterate_path( path, mymkdir, NULL );
    }
    
    int main( int argc, const char** argv )
    {
        size_t i;
        int rv;
    
        if( argc < 2 )
        {
            fprintf( stderr, "usage: %s <path> [<path>...]\n",
                     argv[ 0 ] );
            exit( 1 );
        }
    
        for( i = 1; i < argc; i++ )
        {
            rv = mkdir_with_leading( argv[ i ] );
            if( 0 != rv )
                return rv;
        }
    
        return 0;
    }
    
  • 0

    给出的另外两个答案是针对 mkdir(1) 而不是 mkdir(2) ,但您可以查看该程序的the source code,看看它如何实现 -p 选项,根据需要重复调用 mkdir(2) .

  • 67

    看一下bash源代码here,具体看一下examples / loadables / mkdir.c,特别是136-210行 . 如果你没有't want to do that, here'处理这个问题的一些来源(直接来自我链接的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;
    }
    

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

  • 0

    其实你可以使用:

    mkdir -p ./some/directories/to/be/created/
    
  • 9

    这是我的解决方案 . 通过调用下面的函数,确保存在指向文件路径的所有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);
      char* p;
      for (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;
    }
    

    请注意, file_path 在操作期间被修改,但之后会恢复 . 因此 file_path 并非严格 const .

  • 0

    我不允许对第一个(并接受的)答案发表评论(不够代表),所以我会在新答案中将我的评论作为代码发布 . 下面的代码基于第一个答案,但修复了一些问题:

    • 如果使用零长度路径调用,则不会在数组 opath[] 开头之前读取或写入字符(是的,"why would you call it that way?",但另一方面"why would you not fix the vulnerability?")

    • opath 的大小现在是 PATH_MAX (这不是完美的,但优于常数)

    • 如果路径与 sizeof(opath) 一样长或更长,则在复制时正确终止( strncpy() 不执行)

    • 您可以指定写入目录的模式,就像使用标准 mkdir() 一样(尽管如果指定非用户可写或非用户可执行,则递归将不起作用)

    • main()返回(required?)int

    • 删除了一些不必要的 #include

    • 我更喜欢功能名称;)

    // Based on http://nion.modprobe.de/blog/archives/357-Recursive-directory-creation.html
    #include <string.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <limits.h>
    
    static void mkdirRecursive(const char *path, mode_t mode) {
        char opath[PATH_MAX];
        char *p;
        size_t len;
    
        strncpy(opath, path, sizeof(opath));
        opath[sizeof(opath) - 1] = '\0';
        len = strlen(opath);
        if (len == 0)
            return;
        else if (opath[len - 1] == '/')
            opath[len - 1] = '\0';
        for(p = opath; *p; p++)
            if (*p == '/') {
                *p = '\0';
                if (access(opath, F_OK))
                    mkdir(opath, mode);
                *p = '/';
            }
        if (access(opath, F_OK))         /* if path is not terminated with / */
            mkdir(opath, mode);
    }
    
    
    int main (void) {
        mkdirRecursive("/Users/griscom/one/two/three", S_IRWXU);
        return 0;
    }
    
  • 19

    一个非常简单的解决方案,只需输入输入: mkdir dirname

    void execute_command_mkdir(char *input)
    {
         char rec_dir[500];
         int s;
         if(strcmp(input,"mkdir") == 0)
            printf("mkdir: operand required");
        else
         {
            char *split = strtok(input," \t");
            while(split)
            {
                if(strcmp(split,"create_dir") != 0)
                    strcpy(rec_dir,split);
                split = strtok(NULL, " \t");
            }
            char *split2 = strtok(rec_dir,"/");
            char dir[500];
            strcpy(dir, "");
            while(split2)
            {
                strcat(dir,split2);
                strcat(dir,"/");
                printf("%s %s\n",split2,dir);
                s = mkdir(dir,0700);
                split2 = strtok(NULL,"/");
            }
            strcpy(output,"ok");
        }
            if(s < 0)
                printf(output,"Error!! Cannot Create Directory!!");
    }
    
  • 0

    嗯,我以为mkdir -p会这样做吗?

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

相关问题