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?
Rekursiver mkdir()-Systemaufruf unter Unix
Alex Marschall
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 anstrlen()
ist überflüssig.len = snprintf(tmp, ...
. Auf diese Weise können Sie auf Pufferüberlauf prüfenif(len >= sizeof tmp)
. Mit demstrlen()
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, daPATH_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
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
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.
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.
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 jetztPATH_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 (wasstrncpy()
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
#include
s - 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
Bitte fügen Sie keine weiteren Implementierungen dieser Funktion als Antworten hinzu.
– Jason C
9. Mai um 1:42