Rekursiver mkdir()-Systemaufruf unter Unix

Lesezeit: 8 Minuten

Benutzeravatar von Alex Marshall
Alex Marschall

Nach dem Lesen der Manpage mkdir(2) für den Unix-Systemaufruf mit diesem Namen scheint es, 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?

  • Bitte fügen Sie keine weiteren Implementierungen dieser Funktion als Antworten hinzu.

    – Jason C

    9. Mai um 1:42

Benutzeravatar von Carl Norum
Karl Norum

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 dafür zu haben, was in Fehlerfällen passieren sollte. Soll es die bereits angelegten Verzeichnisse belassen? Lösche sie? Was ist, wenn die Löschungen fehlschlagen? Usw…

Es ist jedoch ziemlich einfach, Ihre eigenen zu rollen, und ein schnelles Googlen für ‘rekursives mkdir“ ergaben sich eine Reihe von Lösungen. Hier ist einer, der ganz oben war:

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] == "https://stackoverflow.com/")
        tmp[len - 1] = 0;
    for (p = tmp + 1; *p; p++)
        if (*p == "https://stackoverflow.com/") {
            *p = 0;
            mkdir(tmp, S_IRWXU);
            *p = "https://stackoverflow.com/";
        }
    mkdir(tmp, S_IRWXU);
}

  • Das einzige was ich ändern würde ist tmp[256] zu tmp[PATH_MAX] auch #include

    – rauer

    2. März 2015 um 16:37 Uhr


  • Verbesserte Version: gist.github.com/JonathonReinhart/…

    – Jonathon Reinhart

    14. Mai 2016 um 3:35 Uhr

  • snprintf() gibt die Länge des formatierten Strings zurück, der Aufruf an strlen() ist überflüssig. len = snprintf(tmp, ... . Auf diese Weise können Sie auf Pufferüberlauf prüfen if(len >= sizeof tmp). Mit dem strlen() es ist nicht möglich.

    – Patrick Schlüter

    23. Mai 2016 um 7:41 Uhr

  • Was ist, wenn mkdir in loop kein Verzeichnis erstellen konnte? Zum Beispiel von Berechtigungen? Ich würde die erwähnte Version von github dringend empfehlen gist.github.com/JonathonReinhart/…

    – iwtu

    12. September 2016 um 13:36 Uhr


  • @rouzier PATH_MAX kann keine Verbesserung sein, da PATH_MAX wird nicht definiert POSIX-kompatible Systeme, bei denen der Wert zwischen verschiedenen Dateisystemen variiert (fette Mine): “Eine Definition einer der symbolischen Konstanten in der folgenden Liste entfallen von dem <limits.h> Header bei bestimmten Implementierungen, bei denen der entsprechende Wert gleich oder größer als das angegebene Minimum ist, der Wert jedoch je nach Datei, auf die er angewendet wird, variieren kann.”

    – Andreas Henle

    20. Juli 2021 um 19:28 Uhr


hmm ich dachte das macht mkdir -p das?

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

  • Ja, aber die Frage bezieht sich auf einen C-Funktionsaufruf.

    – Craig McQueen

    19. Februar 2013 um 2:42 Uhr

  • In der Tat – die positiven Stimmen spiegeln vermutlich wider, dass dies für viele eine nützliche Antwort war, aber es ist eine Antwort auf eine andere Frage als die, die gestellt wurde.

    – Chris Stratton

    7. Oktober 2014 um 18:20 Uhr


  • Man könnte sich jedoch den Quellcode für mkdir ansehen, um zu sehen, wie das geht es macht es. Wenn Sie schnell googlen, scheint der relevante Code enthalten zu sein mkancestdirs.c in coreutils

    – gamen

    31. März 2015 um 9:52 Uhr


  • Dies ist eine Antwort auf eine andere gestellte Frage.

    – Nenad Radulović

    24. Oktober 2016 um 12:57 Uhr

  • @JasonC Vielleicht, obwohl mit system("mkdir -p ...") ist riskant, wenn der Verzeichnisname eine Benutzereingabe ist; dann erfordert die Benutzereingabe eine sorgfältige Bereinigung/Validierung. Schwachstelle in Funktion C system() – Antworten Hier

    – Craig McQueen

    9. Mai um 1:41 Uhr

Benutzeravatar von Yaroslav Stavnichiy
Jaroslaw Stavnichiy

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

Z.B., mkpath("/home/me/dir/subdir/file.dat", 0755) schaffen soll /home/me/dir/subdir wenn es nicht existiert. mkpath("/home/me/dir/subdir/", 0755) macht das gleiche.

Funktioniert auch mit relativen Pfaden.

Kehrt zurück -1 und setzt errno im Falle eines Fehlers.

int mkpath(char* file_path, mode_t mode) {
    assert(file_path && *file_path);
    for (char* p = strchr(file_path + 1, "https://stackoverflow.com/"); p; p = strchr(p + 1, "https://stackoverflow.com/")) {
        *p = '\0';
        if (mkdir(file_path, mode) == -1) {
            if (errno != EEXIST) {
                *p = "https://stackoverflow.com/";
                return -1;
            }
        }
        *p = "https://stackoverflow.com/";
    }
    return 0;
}

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

  • besser als die akzeptierte Antwort; mit Fehlerbehandlung!

    – Chris Maes

    20. Februar 2015 um 11:42 Uhr

  • Das einzige Problem ist, dass es eine Nicht-Konstante verwendet char * als Parameter, weil es den Inhalt des ursprünglichen Zeigers ändert. Dies ist nicht ideal, da es beispielsweise nicht mit statischen konstanten Zeichenfolgen funktioniert und eine unnötige API-Anforderung hat.

    – Felipe Tonello

    17. Juni 2021 um 3:23 Uhr

  • @FelipeTonello Sie können ganz einfach Speicher zuweisen und eine Kopie des Parameters erstellen, wie in anderen Antworten vorgeschlagen. Mein Ziel war die Leistung, also habe ich versucht, teure Operationen wie die Speicherzuweisung zu vermeiden.

    – Jaroslaw Stavnichiy

    18. Juni 2021 um 18:59 Uhr

Benutzeravatar von troglobit
Troglobit

Hier ist eine andere Einstellung mkpath(), mit Rekursion, die sowohl klein als auch lesbar ist. Es nutzt strdupa() um das Gegebene nicht zu verändern dir String-Argument direkt und um die Verwendung zu vermeiden malloc() & free(). Achten Sie darauf, mit zu kompilieren -D_GNU_SOURCE aktivieren strdupa() … was 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] == "https://stackoverflow.com/")
        return 0;

    mkpath(dirname(strdupa(dir)), mode);

    return mkdir(dir, mode);
}

Nach Eingaben sowohl hier als auch von Valery Frolov, im Inadyn-Projekt, die folgende überarbeitete Version von mkpath() wurde nun dazu geschoben freizügigkeit

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 verwendet einen weiteren Systemaufruf, aber otoh, der Code ist jetzt besser lesbar.

Benutzeravatar von Chinmay Kanchi
Chinmay Kanchi

Schauen Sie sich hier den Bash-Quellcode an, und schauen Sie insbesondere in example/loadables/mkdir.c nach, insbesondere in den Zeilen 136-210. Wenn Sie das nicht tun möchten, finden Sie hier einige der Quellen, die sich damit befassen (direkt aus der tar.gz, die 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 == "https://stackoverflow.com/")
    p++;

  while (p = strchr (p, "https://stackoverflow.com/"))
  {
      *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++ = "https://stackoverflow.com/";   /* restore slash */
      while (*p == "https://stackoverflow.com/")
          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.

Anscheinend nicht, meine zwei Vorschläge sind:

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

Oder wenn Sie nicht verwenden möchten system() schau dir mal die coreutils an mkdir Quellcode und sehen, wie sie das implementiert haben -p Möglichkeit.

Benutzeravatar von Daniel Griscom
Daniel Gricom

Ich darf die erste (und akzeptierte) Antwort nicht kommentieren (nicht genug Wiederholung), also poste ich meine Kommentare als Code in einer neuen Antwort. Der folgende Code basiert auf der ersten Antwort, behebt jedoch eine Reihe von Problemen:

  • Wenn es mit einem Pfad der Länge Null aufgerufen wird, liest oder schreibt es das Zeichen vor dem Anfang des Arrays nicht opath[] (ja, “warum würden Sie es so nennen?”, aber andererseits “warum würden Sie die Schwachstelle nicht beheben?”)
  • die Größe von opath ist jetzt PATH_MAX (was nicht perfekt ist, aber besser als eine Konstante)
  • wenn der Weg so lang oder länger als ist sizeof(opath) dann wird es beim Kopieren ordnungsgemäß beendet (was strncpy() geht nicht)
  • Sie können den Modus des geschriebenen Verzeichnisses genau wie beim Standard festlegen mkdir() (Wenn Sie jedoch nicht vom Benutzer beschreibbare oder nicht vom Benutzer ausführbare Dateien angeben, funktioniert die Rekursion nicht.)
  • main() gibt den (erforderlichen?) int zurück
  • ein paar unnötige entfernt #includes
  • 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] == "https://stackoverflow.com/")
        opath[len - 1] = '\0';
    for(p = opath; *p; p++)
        if (*p == "https://stackoverflow.com/") {
            *p = '\0';
            if (access(opath, F_OK))
                mkdir(opath, mode);
            *p = "https://stackoverflow.com/";
        }
    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;
}

  • Ich würde dies ändern, um int zurückzugeben. es ist also nur eine 1-Wort-Änderung zum Refactoring.

    – Jaybny

    8. September 2016 um 8:56 Uhr

1417830cookie-checkRekursiver mkdir()-Systemaufruf unter Unix

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy