81 votes

Récursive mkdir() de l'appel système Unix

Après la lecture de la mkdir(2) page de man pour le système Unix appel avec ce nom, il semble que l'appel n'est pas de créer des répertoires intermédiaires dans un chemin d'accès, seul le dernier répertoire dans le path. Est-il possible (ou d'autres) pour créer tous les répertoires dans le chemin sans avoir recours à l'manuellement l'analyse de mon répertoire de chaîne et individuellement de la création de chaque répertoire ?

113voto

Carl Norum Points 114072

Il n'est pas un appel système pour le faire pour vous, malheureusement. Je suppose que c'est parce qu'il n'y a pas un moyen d'avoir vraiment sémantique bien définie pour ce qui devrait se produire en cas d'erreur. Faut-il laisser les répertoires qui ont déjà été créés? Les supprimer? Que faire si les suppressions d'échouer? Et ainsi de suite...

Il est assez facile de rouler votre propre, cependant, et un rapide google pour"récursive mkdir' mis en place un certain nombre de solutions. Voici un qui était près du sommet:

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);
}

69voto

j03m Points 1684

hmm je pensais que mkdir -p est-ce que?

mkdir -p cette/est/a/full/chemin/de/stuff

30voto

Voici ma solution. En appelant la fonction ci-dessous vous assurer que tous les dirs menant au chemin d'accès au fichier spécifié existe. Notez que file_path argument n'est pas de répertoire nom ici, mais plutôt un chemin vers un fichier que vous allez créer après l'appel de mkpath().

Eg., mkpath("/home/me/dir/subdir/file.dat", 0755) créer /home/me/dir/subdir si elle n'existe pas. mkpath("/home/me/dir/subdir/", 0755) fait de même.

Fonctionne avec des chemins relatifs, ainsi.

Les retours -1 et définit errno dans le cas d'une erreur.

int mkpath(char* file_path, mode_t mode) {
  assert(file_path && *file_path);
  char* p;
  for (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;
}

Notez que file_path est modifié au cours de l'action, mais est restauré par la suite. Par conséquent, file_path n'est pas strictement const.

9voto

Chinmay Kanchi Points 16353

Jetez un oeil à la source de bash code ici, et en particulier dans les exemples/loadables/mkdir.c surtout les lignes 136-210. Si vous ne voulez pas le faire, voici une partie de la source qui traite de ce (pris directement à partir de la tar.gz que j'ai mis en lien):

/* 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;
}

Vous pouvez probablement vous en sortir avec moins de mise en œuvre.

6voto

SiegeX Points 32614

Apparemment pas, mes deux suggestions:

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

Ou si vous ne souhaitez pas utiliser system() essayer de regarder les coreutils mkdir source code et de voir comment ils ont mis en œuvre l' -p option.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X