在C语言中创建新目录

129

我想编写一个程序来检查目录是否存在,如果目录不存在,则创建该目录和其中的一个日志文件,但如果目录已经存在,则只需在该文件夹中创建一个新的日志文件。

如何在Linux下使用C语言实现这个功能?


1
mkdir函数创建一个新目录,http://blog.tremend.ro/2008/10/06/create-directories-in-c-using-mkdir-with-proper-permissions/ - fsonmezay
1
也许是因为你可以在谷歌或者这里通过简单搜索找到解决方案,http://stackoverflow.com/search?q=C+make+directory。顺便说一句,我不是那个给你点踩的人。 - fsonmezay
请[编辑]您的问题以展示您目前所拥有的代码。您应该至少包含一个概述(最好是[mcve])您遇到问题的代码,这样我们才能尝试解决具体的问题。您还应该阅读[提问的智慧]。 - Toby Speight
1
@TobySpeight,这不是对这个问题的好建议。这个问题太过宽泛,而且没有“具体问题”没有被包含在问题中。问题是要找到一种在C中创建目录的方法。将问题与OP的特定代码混淆在一起可能会大大降低适用于许多其他用户的可行性。 - Kröw
5个回答

194

查看stat以检查目录是否存在,以及使用mkdir创建目录。

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

struct stat st = {0};

if (stat("/some/directory", &st) == -1) {
    mkdir("/some/directory", 0700);
}

你可以使用 man 2 statman 2 mkdir 命令查看这些函数的手册。


9
我相信在Linux下,除了路径(path)之外,mkdir 命令需要第二个参数 mode - Paul R
3
@Uku:向函数传递不正确数量的参数是未定义行为,因此尽管它在某些情况下可能对您有效,但您不应依赖它。 - Paul R
5
在创建目录之前检查目录不存在的目的是什么?即使 stat 命令显示该目录尚不存在,另一个进程在此期间也可能已经创建了该目录。 - Brandin
3
我想你是在回答原帖的问题时盲目作答了 :) 你对于竞争条件是正确的。 - Arnaud Le Blanc
6
这将被大多数良好的静态分析器标记为TOCTOU风险。 - kdopen
显示剩余8条评论

34
你可以使用 mkdir 命令: $ man 2 mkdir
#include <sys/stat.h>
#include <sys/types.h>

int result = mkdir("/home/me/test.txt", 0777);

1
这会删除并替换现有的目录吗? - jjxtra
1
@jjxtra:: 不,如果目录已经存在,它应该失败,就像你从命令行尝试执行相同操作一样。 - Paul R
这正是我所猜测的。跳过状态检查而每次只是创建一个mkdir会有任何性能问题吗? - jjxtra
@jjxtra:除非你正在创建数千个目录,否则我无法想象会有任何可测量的性能差异。 - Paul R
7
如果要创建的目录不存在,直接尝试使用mkdir()比先进行stat()操作更快,因为它可以节省一个系统调用。如果目录已经存在,则成功的stat()比不成功的mkdir()慢,因为stat()需要执行更多工作才能完成。 - Kai Petzke

11
我想写一个程序,它可以创建一个目录,并在其中创建一个文件。因为这是一个非常常见的问题,所以这里是创建多级目录并调用fopen的代码。我使用了GNU扩展来使用printf打印错误消息。
void rek_mkdir(char *path) {
    char *sep = strrchr(path, '/');
    if(sep != NULL) {
        *sep = 0;
        rek_mkdir(path);
        *sep = '/';
    }
    if(mkdir(path, 0777) && errno != EEXIST)
        fprintf(stderr, "error while trying to create '%s'\n%m\n", path); 
}

FILE *fopen_mkdir(char *path, char *mode) {
    char *sep = strrchr(path, '/');
    if(sep) { 
        char *path0 = strdup(path);
        path0[ sep - path ] = 0;
        rek_mkdir(path0);
        free(path0);
    }
    return fopen(path,mode);
}

4
仅仅是我的个人建议——对于文件夹来说,权限值0777可能不是最好的选择,0755可能更合适,或者考虑通过参数传递设置权限。 - ivan.ukr
2
@ivan.ukr 0777是正确的,模式将由用户的umask修改。例如,对于umask 022,结果为755,对于umask 007,则为770。 - imix
不要忘记,如果指定的路径名已经存在一个非目录文件,mkdir 命令会返回 EEXIST,所以我们需要对此进行测试。 - undefined

7

int mkdir (const char *filename, mode_t mode)

#include <sys/types.h>
#include <errno.h>
#include <string.h>

if (mkdir("/some/directory", S_IRWXU | S_IRWXG | S_IRWXO) == -1) {
    printf("Error: %s\n", strerror(errno));
}

为了最佳实践,建议使用一个整型别名来代替mode。参数mode指定新目录的文件权限。

读 + 写 + 执行:S_IRWXU(用户)、S_IRWXG(组)、S_IRWXO(其他人)

来源: https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html

如果想要确定该目录是否已经存在,请查找错误号 EEXIST。


为什么不使用perror()呢?这样可以避免错误地将内容写入stdout而不是stderr,这是其中一个好处! - undefined
为了最佳实践,建议使用整数别名来表示模式。这完全是不必要的。这些别名的值是由POSIX规范确定的(我加粗了):“<sys/stat.h>头文件应定义以下符号常量,用于在类型mode_t中编码的文件模式位,并指定了相应的数值。” - undefined

0
这是 Jens Harms 代码的简化版本,不使用递归。
FILE * fopen_mkdir(const char *path, const char *mode)
{
    char *p = strdup(path);
    char *sep = strchr(p+1, '/');
    while(sep != NULL)
    {
        *sep = '\0';
        if (mkdir(p, 0755) && errno != EEXIST)
        {
            fprintf(stderr, "error while trying to create %s\n", p);
        }
        *sep = '/';
        sep = strchr(sep+1, '/');
    }
    free(p);
    return fopen(path, mode);
}

1
确实,这是很好的代码。但用户要求在所需的答案中包括日志文件的创建,而我在你的代码中没有看到这一点。公平地说,我认为其他人也都忽略了这一部分,所以我绝对不会扣分你的回答。谢谢... - undefined
1
文件是通过调用函数 FILE *f = fopen_mkdir("/path/to/log.txt", "w"); 创建的。 - undefined
你为什么认为在这里使用0777是合适的呢?我看不出无论umask如何,都给予全局写权限的理由,我会使用0755来代替。 - undefined
1
@TobySpeight 根据Jens Harms回答下的评论和阅读了你的评论后,我使用了0777。你的建议似乎很合理,我相应地编辑了我的回答。谢谢! - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接