it-swarm.com.de

Rekursiver Systemaufruf von mkdir () unter Unix

Nach dem Lesen der Manpage mkdir (2) für den Unix-Systemaufruf mit diesem Namen wird angezeigt, dass der Aufruf keine Zwischenverzeichnisse in einem Pfad erstellt, sondern nur das letzte Verzeichnis im Pfad. Gibt es eine Möglichkeit (oder eine andere Funktion), alle Verzeichnisse im Pfad zu erstellen, ohne meine Verzeichniszeichenfolge manuell zu analysieren und jedes Verzeichnis einzeln zu erstellen?

62
Alex Marshall

Leider gibt es keinen Systemaufruf, der dies für Sie erledigt. Ich vermute, das liegt daran, dass es keine Möglichkeit gibt, eine wirklich gut definierte Semantik für das zu haben, was in Fehlerfällen passieren sollte. Soll es die bereits erstellten Verzeichnisse belassen? Lösche sie? Was ist, wenn die Löschungen fehlschlagen? Und so weiter...

Es ist jedoch ziemlich einfach, eine eigene Version zu erstellen, und mit einem schnellen Google-Befehl für ' rekursives mkdir ' wurden eine Reihe von Lösungen gefunden. Hier ist eine, die sich in der Nähe der Spitze befand:

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);
}
85
Carl Norum

hmm ich dachte mkdir -p macht das?

mkdir -p dies/ist/ein/vollständiger/Pfad/von/Zeug

66
j03m

Hier ist meine Lösung. Durch Aufrufen der folgenden Funktion stellen Sie sicher, dass alle Verzeichnisse vorhanden sind, die zum angegebenen Dateipfad führen. Beachten Sie, dass das Argument file_path hier kein Verzeichnisname ist, sondern ein Pfad zu einer Datei, die Sie nach dem Aufruf von mkpath() erstellen.

ZB soll mkpath("/home/me/dir/subdir/file.dat", 0755)/home/me/dir/subdir erstellen, wenn es nicht existiert. mkpath("/home/me/dir/subdir/", 0755) macht dasselbe.

Funktioniert auch mit relativen Pfaden.

Gibt -1 zurück und setzt errno im Fehlerfall.

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

Beachten Sie, dass file_path während der Aktion geändert wird, danach jedoch wiederhergestellt wird. Daher ist file_path nicht streng const.

22

Hier ist eine andere Einstellung von mkpath(), bei der eine Rekursion verwendet wird, die sowohl klein als auch lesbar ist. Es verwendet strdupa(), um zu vermeiden, dass das angegebene String-Argument dir direkt geändert wird, und um die Verwendung von malloc() & free() zu vermeiden. Stellen Sie sicher, dass Sie mit -D_GNU_SOURCE Kompilieren, um strdupa() zu aktivieren. Dies bedeutet, dass dieser Code nur auf GLIBC-, EGLIBC-, uClibc- und anderen GLIBC-kompatiblen C-Bibliotheken funktioniert.

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

Nach Eingaben von Valery Frolov im Inadyn-Projekt wurde die folgende überarbeitete Version von mkpath() jetzt auf libite verschoben

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

Es wird ein weiterer syscall verwendet, aber der Code ist jetzt besser lesbar.

11
troglobit

Werfen Sie einen Blick auf den Bash-Quellcode hier und schauen Sie sich insbesondere die Beispiele/loadables/mkdir.c an, insbesondere die Zeilen 136-210. Wenn Sie das nicht möchten, finden Sie hier einige Quellen, die sich damit befassen (direkt aus dem tar.gz entnommen, das ich verlinkt habe):

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

Sie können wahrscheinlich mit einer weniger allgemeinen Implementierung davonkommen.

9
Chinmay Kanchi

Anscheinend nicht, meine beiden Vorschläge sind:

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

Wenn Sie system() nicht verwenden möchten, schauen Sie sich den Quellcode von coreutils mkdir an und sehen Sie, wie die Option -p Implementiert wurde.

8
SiegeX

Eigentlich kann man einfach benutzen:

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

Meine rekursive Vorgehensweise:

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

BEARBEITEN: Mein altes Code-Snippet wurde korrigiert, Fehlerbericht von Namchester

1
Kamiccolo

Ich darf die erste (und akzeptierte) Antwort nicht kommentieren (nicht genügend Wiederholungen), daher werde ich meine Kommentare als Code in einer neuen Antwort veröffentlichen. Der folgende Code basiert auf der ersten Antwort, behebt jedoch eine Reihe von Problemen:

  • Bei einem Aufruf mit einem Pfad der Länge Null wird das Zeichen vor dem Beginn des Arrays opath[] Weder gelesen noch geschrieben (ja, "warum würden Sie es so nennen?", Aber andererseits "warum" Sie haben die Schwachstelle nicht behoben? ")
  • die Größe von opath ist jetzt PATH_MAX (was nicht perfekt ist, aber besser als eine Konstante)
  • wenn der Pfad so lang oder länger als sizeof(opath) ist, wird er beim Kopieren ordnungsgemäß beendet (was strncpy() nicht tut)
  • sie können den Modus des geschriebenen Verzeichnisses genau wie bei der Standardfunktion mkdir() angeben.
  • main () gibt den (erforderlichen?) int zurück
  • einige unnötige #include entfernt
  • Mir gefällt der Funktionsname besser;)
// 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;
}
1
Daniel Griscom

Hier ist meine Ansicht zu einer allgemeineren Lösung:

#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;
}
0

Ganz gerade. Dies kann ein guter Ausgangspunkt sein

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

Sie analysieren diese Funktion, indem Sie einen vollständigen Pfad und die gewünschten Berechtigungen S_IRUSR eingeben. Eine vollständige Liste der Modi finden Sie hier https: // techoverflow.net/2013/04/05/how-to-use-mkdir-from-sysstat-h/

Die vollständige Pfadzeichenfolge wird durch das Zeichen "/" geteilt und einzelne Verzeichnisse werden nacheinander an die aggrpaz Zeichenfolge angehängt. Jede Schleifeniteration ruft die Funktion mkdir auf und übergibt ihr den gesamten Pfad sowie die Berechtigungen. Dieses Beispiel kann verbessert werden, ich überprüfe die Ausgabe der mkdir-Funktion nicht und diese Funktion funktioniert nur mit absoluten Pfaden.

0
Daniel J.

Die beiden anderen Antworten beziehen sich auf mkdir(1) und nicht auf mkdir(2), wie Sie es wünschen, aber Sie können sich den Quellcode für dieses Programm ansehen und sehen, wie es funktioniert Implementiert die Optionen -p, die mkdir(2) nach Bedarf wiederholt aufrufen.

0
hlovdal

Meine Lösung:

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;
}
0
user798267

Eine sehr einfache Lösung, geben Sie einfach die Eingabe ein: 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!!");
}
0
Harbeer Singh