ProgramingTip

Unix에서 재귀 적 mkdir () 시스템 호출

bestdevel 2020. 11. 22. 20:20
반응형

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 this / is / a / full / path / of / stuff


여기 내 해결책이 있습니다. 아래 함수를 호출하여 지정된 파일 경로로 이어지는 모든 디렉토리가 존재하는지 확인합니다. 참고 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()이 이제 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);
}

하나 더 시스템 호출을 사용하지만 코드는 이제 더 읽기를 사용합니다.


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

덜 일반적인 구현으로 할 수 있습니다.


내 두 가지 제안은 다음과 가변합니다.

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

또는 사용하지 않고 매장면 system()coreutils mkdir소스 코드를 살펴보고 -p옵션 을 구현 한 방법을 확인하십시오 .


실제로 다음을 사용할 수 있습니다.

mkdir -p ./some/directories/to/be/created/

나는 첫 번째 (그리고 받아 들인) 답변 (반복이 충분하지 않음)에 대해 댓글을 달 수 있습니다. 새 답변의 코드로 내 댓글을 게시하겠습니다. 아래 코드는 첫 번째 답변을 기반으로하지만 여러 문제를 해결합니다.

  • 길이가 0 인 경로로 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의 버그 보고서


주어진 두 개의 다른 답변을위한 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 /

전체 경로 문자열은 "/"문자로 분할되고 개별 diraggrpaz 문자열에 한 번에 하나씩 추가됩니다 . 각 루프 반복은 mkdir 함수를 호출하여 지금까지의 집계 경로와 권한을 전달합니다. 이 예제는 개선 될 수 있습니다. mkdir 함수 출력을 확인하지 않고이 함수는 절대 경로에서만 작동합니다.

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

반응형