programing

Unix에 대한 재귀 mkdir() 시스템 호출

prostudy 2022. 4. 26. 21:49
반응형

Unix에 대한 재귀 mkdir() 시스템 호출

유닉스 시스템 호출에 대한 mkdir(2) man 페이지를 해당 이름으로 읽고 나면 호출이 경로에 중간 디렉토리를 만들지 않고 경로의 마지막 디렉토리만 생성하는 것으로 나타난다.내 디렉토리 문자열을 수동으로 구문 분석하고 각 디렉토리를 개별적으로 작성하지 않고 경로에 모든 디렉토리를 작성할 수 있는 방법(또는 다른 기능)이 있는가?

불행히도 너를 위해 그것을 할 수 있는 시스템 호출은 없다.나는 그것이 오류의 경우에 일어나야 하는 것에 대한 의미론들을 제대로 정의할 수 있는 방법이 없기 때문이라고 추측한다.이미 작성된 디렉토리를 그대로 두어야 하는가?삭제하시겠습니까?삭제에 실패하면 어떻게 하시겠습니까?등등...

하지만, 여러분 스스로 굴리는 것은 꽤 쉬우며, '재발적인 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/for/forces/p

여기 내 해결책이 있다.아래 함수를 호출하여 지정된 파일 경로로 연결되는 모든 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()주지의 변경을 피하다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 프로젝트에서 다음과 같이 개정된 버전의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을 더 사용하지만, oh 이제 코드를 더 읽을 수 있다.

여기에서 bash 소스 코드를 살펴보고, 특히 136-210 라인의 예/하중물/mkdir.c를 자세히 살펴보십시오.그렇게 하고 싶지 않다면, 여기에 이 문제를 다루는 근원이 있다(내가 연결한 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(). 을 보다.mkdir 그 를 .-p옵션의

첫 번째(그리고 수락된) 답변에 대해서는 코멘트를 할 수 없으므로(의원이 충분하지 않음) 새로운 답변에 코드로 글을 올리겠다.아래 코드는 첫 번째 답을 기반으로 하지만 다음과 같은 여러 문제를 수정한다.

  • , 0-이 로의 시작 opath[](예, "왜 그런 식으로 부르시겠습니까?" 그러나 다른 한편으로는 "왜 취약점을 고치지 않으시겠습니까?")
  • 의 크기opath지금PATH_MAX(완벽하지는 않지만 상수보다 낫다)
  • 만큼 sizeof(opath)복사하면 적절히 종료된다(이것은strncpy()하지 않는다)
  • 표준으로 가능한 것과 같이, 서면 디렉토리의 모드를 지정할 수 있다.mkdir()(사용자가 쓸 수 없는 경우 또는 사용자가 쓸 수 없는 경우 재귀는 작동하지 않음)
  • mainst가 int를 반환한다.
  • 불필요한 몇 가지를 제거했다.#includes
  • 함수명이 더 좋다;)
// 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;
}

편집: 남체스터의 이전 코드 조각 버그 리포트 수정

주어진 다른 두 답은 다음에 대한 것이다.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/

전체 경로 문자열은 "/" 문자로 분할되며 개별 dirs는 한 번에 하나씩 aggrpaz 문자열에 추가된다.각 루프 반복은 mkdir 함수를 호출하여, 지금까지 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;
}

참조URL: https://stackoverflow.com/questions/2336242/recursive-mkdir-system-call-on-unix

반응형