views:

5318

answers:

7

I want an easy way to create multiple directories in C++/Linux.

For example I want to save a file lola.file in the directory:

/tmp/a/b/c

but if the directories are not there I want them to be created automagically. A working example would be perfect.

+6  A: 

system("mkdir -p /tmp/a/b/c")

is the shortest way i can think of.

It's not cross-platform but will work under Linux.

ChristopheD
IF your going to give the solution as a shell command, it would be well to mention system (3)
dmckee
True, i just updated the post.
ChristopheD
+1  A: 

mkdir -p /dir/to/the/file

touch /dir/to/the/file/thefile.ending

+8  A: 
#include <sys/types.h>
#include <sys/stat.h>

int status;
...
status = mkdir("/tmp/a/b/c", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);

From here. You may have to do separate mkdirs for /tmp, /tmp/a, /tmp/a/b/ and then /tmp/a/b/c because there isn't an equivalent of the -p flag in the C api. Be sure and ignore the EEXISTS errno while you're doing the upper level ones.

Paul Tomblin
Fun fact: At least Solaris and HP/UX have mkdirp(), although it's clearly not optimal for portability.
Martin Carpenter
that's the point.. that I don't want to call all these functions separately.
Lipis
Calling mkdir a few times will be way, way faster than calling system once.
Paul Tomblin
+2  A: 

The others got you the right answer, but I thought I'd demonstrate another neat thing you can do:

mkdir -p /tmp/a/{b,c}/d

Will create the following paths:

/tmp/a/b/d
/tmp/a/c/d

The braces allow you to create multiple directories at once on the same level of the hierarchy, whereas the -p option means "create parent directories as needed".

rmeador
after seeing Paul's answer, I realize that I (and a lot of other people) misunderstood the question...
rmeador
and I think rmeador is right :)
Lipis
If somebody can Just update this by changing to system("mkdir -p /tmp/a/{b,c}/d"), cause the questions is not about doing it in shell.. but through C++.
Lipis
Is the "{a,b}" format shell-dependent?
Andy
I think {a,b} will work in both sh-derived and csh-derived shells. I'm not sure if it will work in a system() command, though.
Paul Tomblin
@Lipis: doing that via system() is not a good solution to the OP's question. @Andy: I'd never considered that before, but I just tested it by replacing "mkdir -p" with "echo" and it prints out "/tmp/a/b/d /tmp/a/c/d", which suggests it is the shell doing it, not mkdir.
rmeador
@rmeador: if it's not a good solution, do you have something else to suggest? I want to do that through C++... that's my issue not how to do that through shell..
Lipis
@Lipis: there are plenty of other answers here that suggest how to do it through C++. I'm rather fond of Boost, so I like Benoît's answer. Paul's solution should also work, if your embedded system is very constrained. Btw, sorry for not noticing that you ARE the OP in my last comment :)
rmeador
OP?? what is that?
Lipis
@Lipis: OP = "original poster". in this case, that means you, since you asked the question.
rmeador
+2  A: 

You said "C++" but everyone here seems to be thinking "Bash shell."

Check out the source code to gnu mkdir; then you can see how to implement the shell commands in C++.

Jason Cohen
What do you mean "everyone"?
Paul Tomblin
Well system("mkdir...") should do the trick on linux. It's not cross-platform though.
ChristopheD
teaching a man how to catch a fish: +1
Martin Carpenter
ChristopheD: system() is rarely the correct answer.
Martin Carpenter
+12  A: 

Easy with boost.Filesystem : create_directories

create_directories("/tmp/a/b/c");
Benoît
That's kind of nifty. How big an overheard is there including boost in a C++ project?
Paul Tomblin
It's nifty.. but won't work in my project.. it's an embedded linux system.. and I'm a noob so.. :)
Lipis
Well, most boost libraries are header-only, meaning there is no overhead besides what you use. In the case of Boost.Filesystem, it requires compiling though. On my disk, the compiled library weighs ~60KB.
Benoît
@Lipis: please precise what your embedded system is. I believe it should be available on pretty much every linux distribution.
Benoît
@Benoit: I guess you're right... Maybe I'll try it at one point!
Lipis
+5  A: 

Here's a C function that can be compiled with C++ compilers.

/*
@(#)File:           $RCSfile: mkpath.c,v $
@(#)Version:        $Revision: 1.12 $
@(#)Last changed:   $Date: 2008/05/19 00:43:33 $
@(#)Purpose:        Create all directories in path
@(#)Author:         J Leffler
@(#)Copyright:      (C) JLSS 1990-91,1997-98,2001,2005,2008
@(#)Product:        :PRODUCT:
*/

/*TABSTOP=4*/

#include "jlss.h"
#include "emalloc.h"

#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <string.h>
#include "sysstat.h"    /* Fix up for Windows - inc mode_t */

typedef struct stat Stat;

#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
const char jlss_id_mkpath_c[] = "@(#)$Id: mkpath.c,v 1.12 2008/05/19 00:43:33 jleffler Exp $";
#endif /* lint */

static int do_mkdir(const char *path, mode_t mode)
{
    Stat            st;
    int             status = 0;

    if (stat(path, &st) != 0)
    {
        /* Directory does not exist */
        if (mkdir(path, mode) != 0)
            status = -1;
    }
    else if (!S_ISDIR(st.st_mode))
    {
        errno = ENOTDIR;
        status = -1;
    }

    return(status);
}

/**
** mkpath - ensure all directories in path exist
** Algorithm takes the pessimistic view and works top-down to ensure
** each directory in path exists, rather than optimistically creating
** the last element and working backwards.
*/
int mkpath(const char *path, mode_t mode)
{
    char           *pp;
    char           *sp;
    int             status;
    char           *copypath = STRDUP(path);

    status = 0;
    pp = copypath;
    while (status == 0 && (sp = strchr(pp, '/')) != 0)
    {
        if (sp != pp)
        {
            /* Neither root nor double slash in path */
            *sp = '\0';
            status = do_mkdir(copypath, mode);
            *sp = '/';
        }
        pp = sp + 1;
    }
    if (status == 0)
        status = do_mkdir(path, mode);
    FREE(copypath);
    return (status);
}

#ifdef TEST

#include <stdio.h>

int main(int argc, char **argv)
{
    int             i;

    for (i = 1; i < argc; i++)
    {
        if (mkpath(argv[i], 0777) == 0)
            printf("created: %s\n", argv[i]);
        else
            printf("failed to create: %s\n", argv[i]);
    }
    return(0);
}

#endif /* TEST */

The macros STRDUP() and FREE() are error-checking versions of strdup() and free(). The "sysstat.h" header can be replaced by <sys/stat.h>. And "jlss.h" declares mkpath().

Jonathan Leffler
Ok... the result of that one exactly what I wanted..! Can someone tell me if this is faster than system("mkdir -p /tmp/a/b/c").. cause this is so much easier :)
Lipis
It surely is faster than system. System has a lot of overhead involved. Basically, the process has to be forked, then at least two binaries have to be loaded (one will probably be in cache already), on of which will be yet another fork of the other, ...
ypnos
I forgot: And then "mkdir -p" will do at least the same as the code posted above!
ypnos
thanks for the info! works perfect...
Lipis