Après avoir lu la page de manuel mkdir (2) pour l'appel système Unix portant ce nom, il apparaît que l'appel ne crée pas de répertoires intermédiaires dans un chemin, uniquement le dernier répertoire du chemin. Existe-t-il un moyen (ou une autre fonction) de créer tous les répertoires du chemin sans recourir à l'analyse manuelle de ma chaîne de répertoires et à la création individuelle de chaque répertoire?
Malheureusement, il n'y a pas d'appel système pour le faire pour vous. Je suppose que c'est parce qu'il n'y a aucun moyen d'avoir une sémantique vraiment bien définie pour ce qui devrait se produire dans les cas d'erreur. Doit-il laisser les répertoires déjà créés? Supprime-les? Et si les suppressions échouent? Etc...
Cependant, il est assez facile de lancer le vôtre, et un rapide google pour ' mkdir récursif ' a trouvé un certain nombre de solutions. Voici celui 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);
}
hmm je pensais que mkdir -p fait ça?
mkdir -p this/is/a/full/path/of/stuff
Voici ma solution. En appelant la fonction ci-dessous, vous vous assurez que tous les répertoires menant au chemin de fichier spécifié existent. Notez que l'argument file_path
N'est pas le nom du répertoire ici mais plutôt un chemin vers un fichier que vous allez créer après avoir appelé mkpath()
.
Par exemple, mkpath("/home/me/dir/subdir/file.dat", 0755)
doit créer /home/me/dir/subdir
S'il n'existe pas. mkpath("/home/me/dir/subdir/", 0755)
fait de même.
Fonctionne également avec les chemins relatifs.
Renvoie -1
Et définit errno
en cas d'erreur.
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;
}
Notez que file_path
Est modifié pendant l'action mais est restauré par la suite. Par conséquent, file_path
N'est pas strictement const
.
Voici une autre version de mkpath()
, utilisant la récursion, qui est à la fois petite et lisible. Il utilise strdupa()
pour éviter de modifier directement l'argument de chaîne dir
donné et pour éviter d'utiliser malloc()
& free()
. Assurez-vous de compiler avec -D_GNU_SOURCE
Pour activer strdupa()
... ce qui signifie que ce code ne fonctionne que sur GLIBC, EGLIBC, uClibc et d'autres bibliothèques C compatibles GLIBC.
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);
}
Après avoir été saisie ici et par Valery Frolov, dans le projet Inadyn, la version révisée suivante de mkpath()
a maintenant été poussée vers 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);
}
Il utilise un autre syscall, mais le code est maintenant plus lisible.
Jetez un œil au code source bash ici , et regardez spécifiquement dans examples/loadables/mkdir.c en particulier les lignes 136-210. Si vous ne voulez pas faire cela, voici quelques-unes des sources qui traitent de cela (tirées directement du tar.gz que j'ai lié):
/* 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 une implémentation moins générale.
Apparemment non, mes deux suggestions sont:
char dirpath[80] = "/path/to/some/directory";
sprintf(mkcmd, "mkdir -p %s", dirpath);
system(mkcmd);
Ou si vous ne voulez pas utiliser system()
essayez de regarder le code source de coreutils mkdir
et voyez comment ils ont implémenté l'option -p
.
En fait, vous pouvez simplement utiliser:
mkdir -p ./some/directories/to/be/created/
Ma façon récursive de le faire:
#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;
}
EDIT: correction de mon ancien extrait de code, rapport de bogue par Namchester
Je ne suis pas autorisé à commenter la première réponse (et acceptée) (pas assez de représentants), donc je posterai mes commentaires sous forme de code dans une nouvelle réponse. Le code ci-dessous est basé sur la première réponse, mais résout un certain nombre de problèmes:
opath[]
(Oui, "pourquoi l'appelleriez-vous ainsi?", Mais d'un autre côté "pourquoi vous ne corrigez pas la vulnérabilité? ")opath
est maintenant PATH_MAX
(ce qui n'est pas parfait, mais c'est mieux qu'une constante)sizeof(opath)
alors il est correctement terminé lors de la copie (ce que strncpy()
ne fait pas)mkdir()
standard (bien que si vous spécifiez non inscriptible par l'utilisateur ou non exécutable par l'utilisateur, la récursivité ne fonctionnera pas)#include
s inutiles// 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;
}
Voici mon plan sur une solution plus générale:
#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;
}
Assez droit. Cela peut être un bon point de départ
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;
}
Vous analysez cette fonction un chemin complet ainsi que les autorisations que vous souhaitez, c'est-à-dire S_IRUSR , pour une liste complète des modes, allez ici https: // techoverflow.net/2013/04/05/how-to-use-mkdir-from-sysstat-h/
La chaîne fullpath sera divisée par le caractère "/" et des répertoires individuels seront ajoutés à la chaîne aggrpaz une par une. Chaque itération de boucle appelle la fonction mkdir, en lui passant le chemin d'agrégation jusqu'à présent, plus les autorisations. Cet exemple peut être amélioré, je ne vérifie pas la sortie de la fonction mkdir et cette fonction ne fonctionne qu'avec des chemins absolus.
Les deux autres réponses données sont pour mkdir(1)
et non mkdir(2)
comme vous le demandez, mais vous pouvez regarder le code source pour ce programme et voir comment il implémente les options -p
qui appellent mkdir(2)
à plusieurs reprises selon les besoins.
Ma solution:
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;
}
Une solution très simple, il suffit de saisir en entrée: 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!!");
}