mkdir c
Llamada al sistema Recursive mkdir() en Unix (14)
Después de leer la página man de mkdir (2) para la llamada al sistema Unix con ese nombre, parece que la llamada no crea directorios intermedios en una ruta, solo el último directorio en la ruta. ¿Hay alguna forma (u otra función) para crear todos los directorios en la ruta sin recurrir a analizar manualmente mi cadena de directorio y crear cada directorio individualmente?
Aparentemente no, mis dos sugerencias son:
char dirpath[80] = "/path/to/some/directory";
sprintf(mkcmd, "mkdir -p %s", dirpath);
system(mkcmd);
O, si no desea usar system()
intente ver el código fuente de coreutils mkdir
y vea cómo implementaron la opción -p
.
Aquí está mi oportunidad de una solución más general:
#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;
}
Aquí está mi solución. Al llamar a la función a continuación, asegúrese de que existen todos los directorios que conducen a la ruta especificada del archivo. Tenga en cuenta que el argumento file_path
no es el nombre del directorio aquí, sino más bien una ruta a un archivo que va a crear después de llamar a mkpath()
.
Por ejemplo, mkpath("/home/me/dir/subdir/file.dat", 0755)
creará /home/me/dir/subdir
si no existe. mkpath("/home/me/dir/subdir/", 0755)
hace lo mismo.
Funciona con rutas relativas también.
Devuelve -1
y establece errno
en caso de error.
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;
}
Tenga en cuenta que file_path
se modifica durante la acción pero se restaura después. Por lo tanto, file_path
no es estrictamente const
.
Aquí hay otra mkpath()
de mkpath()
, que usa recursividad, que es pequeña y legible. Hace uso de strdupa()
para evitar alterar directamente el argumento dado de cadena dir
y evitar el uso de malloc()
y free()
. Asegúrese de compilar con -D_GNU_SOURCE
para activar strdupa()
... lo que significa que este código solo funciona en GLIBC, EGLIBC, uClibc y otras bibliotecas de C compatibles con 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);
}
Después de la entrada tanto aquí como de Valery Frolov, en el proyecto Inadyn, la siguiente versión revisada de mkpath()
ahora ha sido empujada a 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);
}
Utiliza un syscall más, pero ahora el código es más legible ahora.
Eche un vistazo al código fuente bash here , y específicamente mire en examples / loadables / mkdir.c especialmente las líneas 136-210. Si no quiere hacer eso, aquí hay alguna fuente que trata sobre esto (tomado directamente del archivo tar.gz que he vinculado):
/* 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;
}
Probablemente pueda salirse con la suya con una implementación menos general.
En realidad, puedes usar:
mkdir -p ./some/directories/to/be/created/
Las otras dos respuestas dadas son para mkdir(1)
y no mkdir(2)
como usted pide, pero puede mirar el código fuente de ese programa y ver cómo implementa las opciones -p
que llama a mkdir(2)
repetidamente como necesario.
Mi manera recursiva de hacer esto:
#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;
}
EDITAR: solucionó mi fragmento de código anterior, informe de Namchester de Namchester
Mi solución:
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;
}
Muy recto. Este puede ser un buen punto de partida
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;
}
Puede analizar esta función con una ruta completa más los permisos que desea, es decir, S_IRUSR , para obtener una lista completa de modos, vaya aquí https://techoverflow.net/2013/04/05/how-to-use-mkdir-from-sysstat-h/
La cadena de ruta completa se dividirá por el carácter "/" y los directorios individuales se anexarán a la cadena de aggrpaz de a uno por vez. Cada iteración de bucle llama a la función mkdir, pasándole la ruta agregada hasta el momento más los permisos. Este ejemplo se puede mejorar, no estoy comprobando la salida de la función mkdir y esta función solo funciona con rutas absolutas.
No hay una llamada al sistema para hacerlo por ti, desafortunadamente. Supongo que es porque no hay una manera de tener una semántica bien definida para lo que debería suceder en casos de error. ¿Debería dejar los directorios que ya se han creado? ¿Borra los? ¿Qué pasa si las eliminaciones fallan? Y así...
Sin embargo, es bastante fácil de hacer, y un google rápido para '' mkdir recursivo '' arrojó una serie de soluciones. Aquí hay uno que estaba cerca de la cima:
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);
}
No tengo permiso para comentar la primera respuesta (y aceptada) (no hay suficiente representante), así que publicaré mis comentarios como código en una nueva respuesta. El siguiente código se basa en la primera respuesta, pero soluciona varios problemas:
- Si se llama con una ruta de longitud cero, esto no lee ni escribe el carácter antes del comienzo de la matriz
opath[]
(sí, "¿por qué lo llamarías así?", Pero por otro lado "¿por qué no corregirías? la vulnerabilidad? ") - el tamaño de
opath
ahora esPATH_MAX
(que no es perfecto, pero es mejor que una constante) - si la ruta es tan larga o más larga que
sizeof(opath)
, se termina correctamente cuando se copia (lo questrncpy()
no funciona) - puede especificar el modo del directorio escrito, tal como lo hace con el estándar
mkdir()
(aunque si especifica non-user-writeable o non-user-executable, la recursión no funcionará) - main () devuelve el (requerido?) int
- eliminado algunos
#include
innecesarios - Me gusta el nombre de la función mejor;)
// 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;
}
Una solución muy simple, solo ingrese la entrada: 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!!");
}
hmm, ¿pensé que mkdir -p hace eso?
mkdir -p this / is / a / full / path / of / cosas