Unix での再帰的な mkdir() システム コール
質問
この名前の Unix システム コールの mkdir(2) マニュアル ページを読んだところ、この呼び出しではパス内に中間ディレクトリは作成されず、パス内の最後のディレクトリのみが作成されるようです。ディレクトリ文字列を手動で解析して各ディレクトリを個別に作成することなく、パス内のすべてのディレクトリを作成する方法(または他の機能)はありますか?
解決
残念ながら、それを行うためのシステムコールはありません。それは、エラーの場合に何が起こるべきかについて、本当に明確に定義されたセマンティクスをもつ方法がないためだと思います。すでに作成されているディレクトリはそのままにしておくべきでしょうか?削除しますか?削除に失敗した場合はどうなりますか?等々...
ただし、独自のロールを作成するのは非常に簡単です。「」を検索してください。再帰的な 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本/ IS / A /フル/パス/の/スタッフ
ここに私のソリューションです。あなたの下の関数を呼び出すことで、すべての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;
}
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);
}
syscall が 1 つ増えていますが、コードがさらに読みやすくなりました。
のここでは、bashのソースコードを見てみましょう、具体的には、例/ 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;
}
あなたはおそらくあまり一般的な実装で逃げることができます。
どうやらない、私の2つの提案されます:
char dirpath[80] = "/path/to/some/directory";
sprintf(mkcmd, "mkdir -p %s", dirpath);
system(mkcmd);
それとも、coreutilsのsystem()
ソースコードを見てmkdir
試みを使用して、彼らは-p
オプションを実装方法を確認する必要はありません。
実際にあなただけ使用することができます:
mkdir -p ./some/directories/to/be/created/
ないんですがコメントの最初の()には回答いrepくだ後の私のコメントしてコードを新しい答えです。以下のコードは、最初の答えが、修正の問題:
- 呼び出された場合は、長さゼロの道すが、これは読み込みまたは書き込みの前にある文字の配列の最初に
opath[]
("yes"、"なぜそう呼んでいるそうです。", その一方で"なぜそうな固定の脆弱性?") - サイズ
opath
現在PATH_MAX
(これは完全ではありませんが、その中には定) - 場合には経路がどのより長かっ
sizeof(opath)
それが正しく終了時にコピーされるstrncpy()
ない) - 指定できるモードのディレクトリとしてできる標準
mkdir()
が指定する場合の非ユーザー書き込み可能または非ユーザーの実行を再帰動作しません) - main()を返します(必要ですか?) int
- 外不要
#include
s - 私のように機能名称良;)
// 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 のにより、固定私の古いコードスニペット、バグレポート:EDIT
他の 2 つの回答は次のとおりです。 mkdir(1)
そしてそうではありません mkdir(2)
あなたが求めるように、しかしあなたは見ることができます ソースコード そのプログラムについて、それがどのように実装されているかを確認してください。 -p
を呼び出すオプション mkdir(2)
必要に応じて繰り返し実行します。
私の解決策ます:
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/
フルパス文字列は「/」文字で分割され、個々のディレクトリが アグルパス 文字列を一度に 1 つずつ。各ループ反復では mkdir 関数を呼び出し、これまでの集約パスと権限を渡します。この例は改善の余地があります。私は mkdir 関数の出力をチェックしていません。また、この関数は絶対パスでのみ機能します。