После прочтения man-страницы mkdir (2) для системного вызова Unix с этим именем появляется, что вызов не создает промежуточные каталоги в пути, а только последний каталог в пути. Есть ли способ (или другая функция) для создания всех каталогов на пути, не прибегая к ручному анализу моей строки каталога и индивидуальному созданию каждого каталога?
Рекурсивный системный вызов mkdir() в Unix
Ответ 1
К сожалению, для вас нет системного вызова. Я предполагаю, что потому, что нет способа правильно определить семантику того, что должно происходить в случаях ошибок. Должно ли оно оставить созданные ранее каталоги? Удалить их? Что делать, если удаления не выполняются? И так далее...
Тем не менее, довольно легко сворачивать ваши собственные, а быстрый google для рекурсивный mkdir - появился ряд решений. Здесь один, который был наверху: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);
}
Ответ 2
hmm Я думал, что mkdir -p делает это?
mkdir -p это/есть/a/full/путь/of/stuff
Ответ 3
Вот мое решение. Вызывая функцию ниже, вы гарантируете, что все каталоги, ведущие к указанному пути файла, существуют. Обратите внимание, что аргумент file_path
- это не имя каталога, а путь к файлу, который вы собираетесь создать после вызова mkpath()
.
Например, mkpath("/home/me/dir/subdir/file.dat", 0755)
должен создать /home/me/dir/subdir
если он не существует. mkpath("/home/me/dir/subdir/", 0755)
делает то же самое.
Работает и с относительными путями.
Возвращает -1
и устанавливает errno
в случае ошибки.
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;
}
Обратите внимание, что file_path
изменяется во время действия, но впоследствии восстанавливается. Поэтому file_path
не является строго const
.
Ответ 4
Здесь еще возьмем mkpath()
, используя рекурсию, которая является маленькой и читаемой. Он использует strdupa()
, чтобы избежать прямого изменения заданного аргумента строки dir
и избежать использования malloc()
и free()
. Обязательно скомпилируйте с помощью -D_GNU_SOURCE
, чтобы активировать strdupa()
..., что означает, что этот код работает только с библиотеками GLIBC, EGLIBC, uClibc и другими совместимыми с GLIBC библиотеками C.
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);
}
После ввода как здесь, так и из Валерия Фролова в проект Inadyn следующая пересмотренная версия mkpath()
теперь была нажата на 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);
}
Он использует еще один syscall, но otoh код теперь более читаем.
Ответ 5
Взгляните на bash исходный код здесь, и особенно посмотрите в примерах /loadables/mkdir.c, в частности строки 136- 210. Если вы не хотите этого делать, вот некоторые из источников, которые касаются этого (взяты прямо из tar.gz, с которым я связан):
/* Make all the directories leading up to PATH, then create PATH. Note that
this changes the process 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;
}
Возможно, вам удастся избежать менее общей реализации.
Ответ 6
По-видимому, нет, мои два предложения:
char dirpath[80] = "/path/to/some/directory";
sprintf(mkcmd, "mkdir -p %s", dirpath);
system(mkcmd);
Или если вы не хотите использовать system()
, попробуйте посмотреть исходный код coreutils mkdir
и посмотрите, как они реализовали параметр -p
.
Ответ 7
На самом деле вы можете просто использовать:
mkdir -p ./some/directories/to/be/created/
Ответ 8
Рекурсивный способ сделать это:
#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: исправлен мой старый фрагмент кода, отчет об ошибке Namchester
Ответ 9
Мне не разрешено прокомментировать первый (и принятый) ответ (недостаточно rep), поэтому я отправлю свои комментарии в виде кода в новый ответ. Код ниже основан на первом ответе, но устраняет ряд проблем:
- Если вы вызываете путь с нулевой длиной, это не читает или не записывает символ перед началом массива
opath[]
(да, "почему бы вы так его назвали?", но, с другой стороны, "почему вы не исправляете уязвимость?" ) - размер
opath
теперьPATH_MAX
(который не является совершенным, но лучше, чем константа) - если путь длинный или длинный, чем
sizeof(opath)
, то он правильно завершается при копировании (которыйstrncpy()
не выполняет) - вы можете указать режим письменной директории, как вы можете, со стандартным
mkdir()
(хотя, если вы укажете исполняемый файл, не подлежащий записи или не выполнимый пользователем, тогда рекурсия не будет работать) - main() возвращает (требуется?) int
- удалил несколько ненужных
#include
s - Мне больше нравится имя функции;)
// 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;
}
Ответ 10
Два других ответа приведены для mkdir(1)
, а не mkdir(2)
, как вы просите, но вы можете посмотреть исходный код для этой программы и посмотреть, как она реализует опции -p
, которые нажимают mkdir(2)
по мере необходимости.
Ответ 11
Мое решение:
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;
}
Ответ 12
Здесь мой снимок более общего решения:
#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 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;
}
Ответ 13
Очень простое решение, просто введите вход: 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!!");
}
Ответ 14
Довольно прямо. Это может быть хорошей отправной точкой
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;
}
Вы анализируете эту функцию как полный путь и требуемые разрешения, т.е. S_IRUSR, для полного списка режимов здесь https://techoverflow.net/2013/04/05/how-to-use-mkdir-from-sysstat-h/
Строка полного пути будет разделена символом "/", и отдельные строки будут добавлены к строке aggrpaz по одному за раз. Каждая итерация цикла вызывает функцию mkdir, передавая ей совокупный путь до сих пор плюс разрешения. Этот пример можно улучшить, я не проверяю вывод функции mkdir, и эта функция работает только с абсолютными путями.