その名前のUnixシステムコールのmkdir(2)マニュアルページを読んだ後、呼び出しはパスに中間ディレクトリを作成せず、パスの最後のディレクトリのみを作成するように見えます。ディレクトリ文字列を手動で解析して各ディレクトリを個別に作成することなく、パス内のすべてのディレクトリを作成する方法(または他の関数)はありますか?
15 に答える
残念ながら、それを行うためのシステムコールはありません。これは、エラーの場合に何が起こるかについて、本当に明確に定義されたセマンティクスを持つ方法がないためだと思います。すでに作成されているディレクトリを残す必要がありますか?それらを削除しますか?削除が失敗した場合はどうなりますか?等々...
ただし、独自のロールを作成するのは非常に簡単です。「再帰的な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);
}
うーん、mkdir -pはそうだと思いましたか?
mkdir -p this / is / a / full / path / of / stuff
これが私の解決策です。以下の関数を呼び出すことにより、指定されたファイルパスにつながるすべてのdirが存在することを確認します。ここで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()
変更しないようにするため、および&を使用しないようにするために使用します。でコンパイルしてアクティブ化してください...つまり、このコードはGLIBC、EGLIBC、uClibc、およびその他のGLIBC互換のCライブラリでのみ機能します。dir
malloc()
free()
-D_GNU_SOURCE
strdupa()
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);
}
こことValeryFrolovの両方からの入力の後、Inadynプロジェクトでは、次の改訂版が解放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);
}
もう1つのシステムコールを使用しますが、コードが読みやすくなりました。
ここで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;
}
おそらく、あまり一般的ではない実装で逃げることができます。
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.
最初の(そして受け入れられた)回答(十分な担当者がいない)にコメントすることは許可されていないので、新しい回答のコードとしてコメントを投稿します。以下のコードは最初の回答に基づいていますが、いくつかの問題を修正しています。
- 長さがゼロのパスで呼び出された場合、配列の開始前に文字の読み取りまたは書き込みは行われません
opath[]
(「なぜそのように呼び出すのですか?」が、「なぜ脆弱性を修正しないのですか?」 ") - のサイズ
opath
は現在ですPATH_MAX
(これは完全ではありませんが、定数よりも優れています) - パスがそれと同じかそれより長い場合
sizeof(opath)
、コピーされたときに適切に終了します(これstrncpy()
は行いません) - 標準の場合と同じように、書き込まれたディレクトリのモードを指定できます
mkdir()
(ただし、非ユーザー書き込み可能または非ユーザー実行可能ファイルを指定した場合、再帰は機能しません) - 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;
}
これを行う私の再帰的な方法:
#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によるバグレポートを修正しました
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.
私の解決策:
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/
フルパス文字列は「/」文字で分割され、個々のdirがaggrpaz文字列に一度に1つずつ追加されます。ループが繰り返されるたびに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")
楽しいから再帰が好きなら!
#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;
}