在阅读了具有该名称的Unix系统调用的mkdir(2)手册页之后,看来该调用不会在路径中创建中间目录,而只会在路径中创建最后一个目录。有什么方法(或其他功能)可以在不手动解析目录字符串并单独创建每个目录的情况下创建路径中的所有目录吗?
Answers:
不幸的是,没有系统调用可以为您完成此操作。我猜这是因为没有一种方法可以对错误情况下的情况真正定义好语义。是否应该保留已经创建的目录?删除它们?如果删除失败怎么办?等等...
推出自己的产品非常容易,但是针对“递归mkdir ”的快速Google提出了许多解决方案。这是接近顶部的一个:
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);
}
snprintf()
返回格式化字符串的长度,对strlen()
多余的调用。len = snprintf(tmp, ...
。您可以通过这种方式检查缓冲区溢出if(len >= sizeof tmp)
。有了strlen()
它是不可能的。
嗯,我认为mkdir -p可以吗?
mkdir -p this / is / a / full / path / of / stuff
coreutils
这是我的解决方案。通过调用下面的函数,可以确保所有指向指定文件路径的目录都存在。请注意,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
。
这是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);
}
在Inadyn项目中从此处和Valery Frolov处输入之后,mkpath()
现在已将以下版本的修订版本推向了自由
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);
}
它使用了另一个系统调用,但是现在该代码更具可读性。
strdupa
都调用此泄漏内存吗?
strdupa()
自动释放。有关更多详细信息,请参见手册页。
strdup
一直都在想。:)
看一下这里的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;
}
您可能可以摆脱一个不太通用的实现。
显然不是,我的两个建议是:
char dirpath[80] = "/path/to/some/directory";
sprintf(mkcmd, "mkdir -p %s", dirpath);
system(mkcmd);
或者,如果您不想使用,请system()
尝试查看coreutilsmkdir
源代码,并查看它们如何实现该-p
选项。
OMG - it's 2010
会更贴切。也许只有我一个人,但是%s
似乎缺乏引号似乎不足以使神灵融入其中。如果您想提出修改建议,请随时提出。
我不允许对第一个(和被接受的)答案(没有足够的代表)发表评论,所以我将在新答案中将我的评论作为代码发布。下面的代码基于第一个答案,但解决了许多问题:
opath[]
(是的,“为什么要这样命名?”,但是另一方面,“为什么不解决该漏洞? ”)opath
现在的大小PATH_MAX
(虽然不完美,但比常数要好)sizeof(opath)
该长度,则在复制时该路径将正确终止(strncpy()
不会这样做)mkdir()
(尽管如果您指定非用户可写或非用户可执行,则递归将不起作用)#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;
}
我这样做的递归方式:
#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的错误报告
给出的另外两个答案是针对您的,mkdir(1)
而不是mkdir(2)
您所要求的,但是您可以查看该程序的源代码,并查看它如何实现根据需要重复-p
调用的选项mkdir(2)
。
make_dir_parents()
函数可能是最有趣的部分,但不在该文件中。它在mkdir-p.c
gnulib存储库中。
我的解决方案:
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;
}
这是我提出的更一般的解决方案:
#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;
}
一个非常简单的解决方案,只需输入: 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!!");
}
挺直的。这可能是一个很好的起点
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函数的输出,并且此函数仅适用于绝对路径。
这是我的解决方案
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")