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

发布于 2024-08-23 07:54:33 字数 126 浏览 10 评论 0原文

阅读具有该名称的 Unix 系统调用的 mkdir(2) 手册页后,该调用似乎不会在路径中创建中间目录,而只会在路径中创建最后一个目录。有没有什么方法(或其他函数)可以创建路径中的所有目录,而无需手动解析我的目录字符串并单独创建每个目录?

After reading the mkdir(2) man page for the Unix system call with that name, it appears that the call doesn't create intermediate directories in a path, only the last directory in the path. Is there any way (or other function) to create all the directories in the path without resorting to manually parsing my directory string and individually creating each directory ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(17

最笨的告白 2024-08-30 07:54:34

我迟到了,但我仍然想分享这个非常简单的功能。我不明白为什么人们把事情搞得这么复杂。

#include <errno.h>
#include <string.h>
#include <sys/stat.h>

int mkdir_p(char *path, mode_t mode)
{
    char *p = path;
    while ((p = strchr(p + 1, '/'))) {
        *p = '\0';
        int ret = mkdir(path, mode);
        *p = '/';
        if (ret == -1 && errno != EEXIST)
            return -1;
    }
    return 0;
}

请注意,mkdir_p("this/is/a/path", 0755) 创建目录"this/is/a/",就好像它是一个文件路径一样。

I'm late to the game, but I still wanted to share this very simple function. I don't get why people make things so complicated.

#include <errno.h>
#include <string.h>
#include <sys/stat.h>

int mkdir_p(char *path, mode_t mode)
{
    char *p = path;
    while ((p = strchr(p + 1, '/'))) {
        *p = '\0';
        int ret = mkdir(path, mode);
        *p = '/';
        if (ret == -1 && errno != EEXIST)
            return -1;
    }
    return 0;
}

Note that mkdir_p("this/is/a/path", 0755) creates the directory "this/is/a/", as if it were a file path.

梦魇绽荼蘼 2024-08-30 07:54:34

给出的其他两个答案是针对 mkdir(1) 而不是您要求的 mkdir(2) ,但您可以查看 该程序的源代码 并查看它如何实现 -p 选项根据需要重复调​​用 mkdir(2)

The two other answers given are for mkdir(1) and not mkdir(2) like you ask for, but you can look at the source code for that program and see how it implements the -p options which calls mkdir(2) repeatedly as needed.

傲世九天 2024-08-30 07:54:34

我的解决方案:

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

My solution:

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;
}
ι不睡觉的鱼゛ 2024-08-30 07:54:34

这是我的一个更通用的解决方案:

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

Here's my shot at a more general solution:

#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;
}
始于初秋 2024-08-30 07:54:34

如果您喜欢递归,因为它很有趣!

#include <string.h>
#include <sys/stat.h> /* mkdir(2) */
#include <limits.h> /* PATH_MAX */    
int mkdirp(const char *dir, const mode_t mode){
        struct stat sb;
        //if dir already exists and is a directory
        if (stat(dir, &sb) == 0){
            if (S_ISDIR(sb.st_mode)) {
                return 0;
            }
            else return -1;
        }
        else {
            char tmp[PATH_MAX];
            size_t len = strnlen(dir, PATH_MAX);
            memcpy(tmp, dir, len);
            //remove trailing slash
            if (tmp[len-1]=='/'){
                tmp[len-1]='\0';
            }
            char *p = strrchr(tmp, '/');
            *p='\0';
            int ret = mkdirp(tmp, mode);
            if (ret == 0){
                return mkdir(dir, mode);
            }
        }
        return 0;
}

If you like recursion because it's fun!

#include <string.h>
#include <sys/stat.h> /* mkdir(2) */
#include <limits.h> /* PATH_MAX */    
int mkdirp(const char *dir, const mode_t mode){
        struct stat sb;
        //if dir already exists and is a directory
        if (stat(dir, &sb) == 0){
            if (S_ISDIR(sb.st_mode)) {
                return 0;
            }
            else return -1;
        }
        else {
            char tmp[PATH_MAX];
            size_t len = strnlen(dir, PATH_MAX);
            memcpy(tmp, dir, len);
            //remove trailing slash
            if (tmp[len-1]=='/'){
                tmp[len-1]='\0';
            }
            char *p = strrchr(tmp, '/');
            *p='\0';
            int ret = mkdirp(tmp, mode);
            if (ret == 0){
                return mkdir(dir, mode);
            }
        }
        return 0;
}
世态炎凉 2024-08-30 07:54:34

一个非常简单的解决方案,只需传入输入: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!!");
}

A very simple solution, just pass in input: 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!!");
}
悲欢浪云 2024-08-30 07:54:34

很直。这可能是一个很好的起点,

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 函数输出,并且该函数仅适用于绝对路径。

Quite straight. This can be a good starting point

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

You parse this function a full path plus the permissions you want, i.e S_IRUSR, for a full list of modes go here https://techoverflow.net/2013/04/05/how-to-use-mkdir-from-sysstat-h/

The fullpath string will be split by the "/" character and individual dirs will be appended to the aggrpaz string one at a time. Each loop iteration calls the mkdir function, passing it the aggregate path so far plus the permissions. This example can be improved, I am not checking the mkdir function output and this function only works with absolute paths.

回梦 2024-08-30 07:54:34

这是我的解决方案

void mkpath(char *p) {
    char *path = strdup(p);
    char  *save_path = path;
    char *sep1;
    char *sep2=0;
    do {
        int idx = (sep2-path)<0 ? 0 : sep2-path;
        sep1 = strchr(path + idx , '/');    
        sep2 = strchr(sep1+1, '/');
        if (sep2) {
            path[sep2-path]=0;
        }
        if(mkdir(path, 0777) && errno != EEXIST)
            break;
        if (sep2) {
            path[sep2-path]='/';
        }
    } while (sep2);

    free(save_path);

}

.
.
.
mkpath ("./the/new/path")

here is my solution

void mkpath(char *p) {
    char *path = strdup(p);
    char  *save_path = path;
    char *sep1;
    char *sep2=0;
    do {
        int idx = (sep2-path)<0 ? 0 : sep2-path;
        sep1 = strchr(path + idx , '/');    
        sep2 = strchr(sep1+1, '/');
        if (sep2) {
            path[sep2-path]=0;
        }
        if(mkdir(path, 0777) && errno != EEXIST)
            break;
        if (sep2) {
            path[sep2-path]='/';
        }
    } while (sep2);

    free(save_path);

}

.
.
.
mkpath ("./the/new/path")
瀞厅☆埖开 2024-08-30 07:54:33

不幸的是,没有系统调用可以为您执行此操作。我猜这是因为没有一种方法可以对错误情况下应该发生的情况有真正明确的语义。它应该保留已经创建的目录吗?删除它们?如果删除失败怎么办?等等...

但是,自己推出非常容易,并且可以快速搜索 '递归 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);
}

There is not a system call to do it for you, unfortunately. I'm guessing that's because there isn't a way to have really well-defined semantics for what should happen in error cases. Should it leave the directories that have already been created? Delete them? What if the deletions fail? And so on...

It is pretty easy to roll your own, however, and a quick google for 'recursive mkdir' turned up a number of solutions. Here's one that was near the top:

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);
}
盛装女皇 2024-08-30 07:54:33

嗯,我认为 mkdir -p 可以做到这一点吗?

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

hmm I thought that mkdir -p does that?

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

暗地喜欢 2024-08-30 07:54:33

这是我的解决方案。通过调用下面的函数,您可以确保指向指定文件路径的所有目录都存在。请注意,这里的 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;
}

请注意,file_path 在操作过程中被修改,但之后会恢复。因此,file_path 并不是严格意义上的const

Here is my solution. By calling the function below you ensure that all dirs leading to the file path specified exist. Note that file_path argument is not directory name here but rather a path to a file that you are going to create after calling mkpath().

Eg., mkpath("/home/me/dir/subdir/file.dat", 0755) shall create /home/me/dir/subdir if it does not exist. mkpath("/home/me/dir/subdir/", 0755) does the same.

Works with relative paths as well.

Returns -1 and sets errno in case of an error.

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

Note that file_path is modified during the action but gets restored afterwards. Therefore file_path is not strictly const.

如日中天 2024-08-30 07:54:33

这是使用递归的 mkpath() 的另一种形式,它既小又可读。它利用 strdupa() 来避免直接更改给定的 dir 字符串参数,并避免使用 malloc() & 。 免费()。确保使用 -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);
}

它又使用了一个系统调用,但代码现在更具可读性。

Here's another take on mkpath(), using recursion, which is both small and readable. It makes use of strdupa() to avoid altering the given dir string argument directly and to avoid using malloc() & free(). Make sure to compile with -D_GNU_SOURCE to activate strdupa() ... meaning this code only works on GLIBC, EGLIBC, uClibc, and other GLIBC compatible C libraries.

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

After input both here and from Valery Frolov, in the Inadyn project, the following revised version of mkpath() has now been pushed to 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);
}

It uses one more syscall, but otoh the code is more readable now.

尝蛊 2024-08-30 07:54:33

此处查看 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;
}

您可能可以使用不太通用的实现。

Take a look at the bash source code here, and specifically look in examples/loadables/mkdir.c especially lines 136-210. If you don't want to do that, here's some of the source that deals with this (taken straight from the tar.gz that I've linked):

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

You can probably get away with a less general implementation.

极致的悲 2024-08-30 07:54:33

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

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

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

Apparently not, my two suggestions are:

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

Or if you don't want to use system() try looking at the coreutils mkdir source code and see how they implemented the -p option.

终止放荡 2024-08-30 07:54:33

我不允许对第一个(和已接受的)答案发表评论(没有足够的代表),因此我将在新答案中将我的评论作为代码发布。下面的代码基于第一个答案,但修复了许多问题:

  • 如果使用零长度路径调用,则不会读取或写入数组 opath[] 开头之前的字符(是的,“为什么要这样称呼它?”,但另一方面“为什么不修复该漏洞?”)
  • opath 的大小现在是 PATH_MAX (这并不完美,但比常量更好)
  • 如果路径与 sizeof(opath) 一样长或长于 sizeof(opath) 那么它在复制时会正确终止(其中 strncpy() 不这样做)
  • 您可以指定写入目录的模式,就像使用标准 mkdir() 一样(尽管如果您指定非用户可写或非用户-executable 那么递归将不起作用)
  • main() 返回(必需?) 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;
}

I'm not allowed to comment on the first (and accepted) answer (not enough rep), so I'll post my comments as code in a new answer. The code below is based on the first answer, but fixes a number of problems:

  • If called with a zero-length path, this does not read or write the character before the beginning of array opath[] (yes, "why would you call it that way?", but on the other hand "why would you not fix the vulnerability?")
  • the size of opath is now PATH_MAX (which isn't perfect, but is better than a constant)
  • if the path is as long as or longer than sizeof(opath) then it is properly terminated when copied (which strncpy() doesn't do)
  • you can specify the mode of the written directory, just as you can with the standard mkdir() (although if you specify non-user-writeable or non-user-executable then the recursion won't work)
  • main() returns the (required?) int
  • removed a few unnecessary #includes
  • I like the function name better ;)
// 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;
}
笑梦风尘 2024-08-30 07:54:33

我这样做的递归方式:

#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

My recursive way of doing this:

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

EDIT: fixed my old code snippet, bug-report by Namchester

自由如风 2024-08-30 07:54:33

无需递归即可简单工作

int mkdir_p(const char *path, int mode)
{
    char *buf = strdup(path);
    char *p = buf;
    int ret = 0;

    if (buf == NULL) {
        return -1;
    }

    mode |= 0700;

    do {
        p = strchr(p + 1, '/');
        if (p) {
            *p = '\0';
        }
        if (mkdir(buf, mode) != 0) {
            if (errno != EEXIST) {
                ret = errno;
                break;
            }
        }
        if (p) {
            *p = '/';
        }
    } while (p);

    free(buf);

    return (ret);
}

int main(int argc, const char *argv[])
{
    mkdir_p("/home/swei/data_xx/algo/trade/session/1", 0666);
}

Simply works without recursion

int mkdir_p(const char *path, int mode)
{
    char *buf = strdup(path);
    char *p = buf;
    int ret = 0;

    if (buf == NULL) {
        return -1;
    }

    mode |= 0700;

    do {
        p = strchr(p + 1, '/');
        if (p) {
            *p = '\0';
        }
        if (mkdir(buf, mode) != 0) {
            if (errno != EEXIST) {
                ret = errno;
                break;
            }
        }
        if (p) {
            *p = '/';
        }
    } while (p);

    free(buf);

    return (ret);
}

int main(int argc, const char *argv[])
{
    mkdir_p("/home/swei/data_xx/algo/trade/session/1", 0666);
}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文