Unix上的递归mkdir()系统调用


70

在阅读了具有该名称的Unix系统调用的mkdir(2)手册页之后,看来该调用不会在路径中创建中间目录,而只会在路径中创建最后一个目录。有什么方法(或其他功能)可以在不手动解析目录字符串并单独创建每个目录的情况下创建路径中的所有目录吗?

Answers:


96

不幸的是,没有系统调用可以为您完成此操作。我猜这是因为没有一种方法可以对错误情况下的情况真正定义好语义。是否应该保留已经创建的目录?删除它们?如果删除失败怎么办?等等...

推出自己的产品非常容易,但是针对“递归mkdir ”的快速Google提出了许多解决方案。这是接近顶部的一个:

http://nion.modprobe.de/blog/archives/357-Recursive-directory-creation.html


8
我唯一要更改的是tmp [256]到tmp [PATH_MAX]也#include <limits.h>
rouzier 2015年


snprintf()返回格式化字符串的长度,对strlen()多余的调用。len = snprintf(tmp, ... 。您可以通过这种方式检查缓冲区溢出if(len >= sizeof tmp)。有了strlen()它是不可能的。
PatrickSchlüter'16

如果mkdir in loop无法创建目录怎么办?例如权限?我强烈建议您从githubgist.github.com/JonathonReinhart/…中
iwtu

69

嗯,我认为mkdir -p可以吗?

mkdir -p this / is / a / full / path / of / stuff


51
是的,的确如此,但是问题与C函数调用有关。
Craig McQueen

11
确实-投票可能反映出这对许多人来说是一个有用的答案,但这是对与所问问题不同的一个答案
克里斯·斯特拉顿

5
但是,可以看看mkdir的源代码以了解工作方式。快速谷歌,似乎相关的代码在mkancestdirs.ccoreutils
gamen 2015年

这是针对不同问题的答案。
Nenad Radulovic

@gamen,您评论中的链接已过期。
merlin2011

26

这是我的解决方案。通过调用下面的函数,可以确保所有指向指定文件路径的目录都存在。请注意,file_path这里的参数不是目录名,而是调用后要创建的文件的路径mkpath()

例如,如果不存在,mkpath("/home/me/dir/subdir/file.dat", 0755)则应创建/home/me/dir/subdirmkpath("/home/me/dir/subdir/", 0755)一样。

也可以使用相对路径。

发生错误时返回-1并设置errno

请注意,file_path该操作在操作期间进行了修改,但之后会恢复。因此file_path不严格const


1
比公认的答案更好;错误处理!
克里斯·梅斯

11

这是mkpath()使用递归的另一种方法,它既小巧又可读。它利用strdupa()来避免dir直接更改给定的字符串参数,并避免使用malloc()free()。确保进行编译-D_GNU_SOURCE以激活strdupa()...,这意味着此代码仅适用于GLIBC,EGLIBC,uClibc和其他GLIBC兼容的C库。

在Inadyn项目中从此处和Valery Frolov处输入之后,mkpath()现在已将以下版本的修订版本推向了自由

它使用了另一个系统调用,但是现在该代码更具可读性。


不会每次strdupa都调用此泄漏内存吗?
nemo 2013年

1
Nope :)当函数脱离上下文时,(在堆栈上)分配的内存将strdupa()自动释放。有关更多详细信息,请参见手册页。
troglobit 2013年

1
啊,对不起,你是对的。我strdup一直都在想。:)
nemo

我知道这是一个非常老的线程,但是我一直在寻找解决方案,因此我很喜欢这个答案,这是我所看到的一切中最好的。但是,这不适用于相对路径。为dir [0] =='添加检查。dir [0] =='/'的检查将纠正此问题,因为这是dirname最终的目的。
gnac '16

这仍然不是理想的,因为它尝试创建“ ..”文件夹,但是仅返回文件存在错误并继续运行。您还可以先执行一个mkdir,并且仅在mkdir返回ENOENT时递归。缺点是您必须在方法中两次调用mkdir。
gnac '16

9

看一下这里的bash源代码,尤其是查看examples / loadables / mkdir.c,尤其是136-210行。如果您不想这样做,这里有一些与此相关的资源(直接取自我链接的tar.gz):

您可能可以摆脱一个不太通用的实现。


7

显然不是,我的两个建议是:

或者,如果您不想使用,请system()尝试查看coreutilsmkdir源代码,并查看它们如何实现该-p选项。


10
OMG-在2014年,文件路径现在通常有空格。请不要这样编码
Lothar 2014年

3
@Lothar也许您没有意识到这个答案是4年前提出的,所以OMG - it's 2010会更贴切。也许只有我一个人,但是%s似乎缺乏引号似乎不足以使神灵融入其中。如果您想提出修改建议,请随时提出。
SiegeX 2014年

1
@Lothar首先,使用system()也是一个不好的选择,它的安全缺陷是没有使用到mkdir的绝对路径,以及错误编码的'char dirpath []'行;每条线都有问题,因此必须降低投票率;对不起SiegeX,但这是一个非常糟糕的答案,但是正如您所说的,这是在2010年;)
Nick

2
/ bin / sh将使用PATH环境变量来定位mkdir。如果名为mkdir的可执行文件位于/ bin之前的路径中,则将改为执行该可执行文件。
尼克,

6
糟糕,缓冲区溢出。你最好希望没有人会创建结尾的路径; RM -rf〜
teambob

1

我不允许对第一个(和被接受的)答案(没有足够的代表)发表评论,所以我将在新答案中将我的评论作为代码发布。下面的代码基于第一个答案,但解决了许多问题:

  • 如果使用零长度路径进行调用,则此操作不会在数组开头之前读取或写入字符opath[](是的,“为什么要这样命名?”,但是另一方面,“为什么不解决该漏洞? ”)
  • opath现在的大小PATH_MAX(虽然不完美,但比常数要好)
  • 如果路径的长度等于或大于sizeof(opath)该长度,则在复制时该路径将正确终止(strncpy()不会这样做)
  • 您可以指定写入目录的方式,就像使用标准方式一样mkdir()(尽管如果您指定非用户可写或非用户可执行,则递归将不起作用)
  • main()返回(required?)int
  • 删除了一些不必要#include
  • 我更喜欢函数名称;)

我将其更改为int。因此只需重构一个字即可。
jaybny

1

我这样做的递归方式:

编辑:修复了我的旧代码段,Namchester的错误报告


3
@Jeff,是的。有什么问题吗?在这种情况下,看起来比很多if或类似的东西更容易理解。它可读性强并且运行良好。
Kamiccolo 2013年

不创建最后一个目录。如果recursive_mkdir(“ / home / test / hello”,mode),则不创建hello目录。
纳曼

@Namchester,显然在绝对路径下效果不佳。定影。
Kamiccolo '16

0

给出的另外两个答案是针对您的,mkdir(1)而不是mkdir(2)您所要求的,但是您可以查看该程序的源代码,并查看它如何实现根据需要重复-p调用的选项mkdir(2)


实际的问题是“有没有办法(或其他功能)在路径中创建所有目录,而无需手动解析我的目录字符串并分别创建每个目录”,因此mkdir(1)是另一种方式!

make_dir_parents()函数可能是最有趣的部分,但不在该文件中。它在mkdir-p.cgnulib存储库中
Craig McQueen

0

我的解决方案:


0

这是我提出的更一般的解决方案:


0

一个非常简单的解决方案,只需输入: mkdir dirname


0

挺直的。这可能是一个很好的起点

您可以为该函数解析完整的路径以及所需的权限,即S_IRUSR,以获取模式的完整列表,请访问此处https://techoverflow.net/2013/04/05/how-to-use-mkdir-from-sysstat- H/

全路径字符串将由“ /”字符分隔,并且单个目录将一次附加到aggrpaz字符串中。每次循环迭代都会调用mkdir函数,并向其传递到目前为止的聚合路径以及权限。此示例可以改进,我不检查mkdir函数的输出,并且此函数仅适用于绝对路径。


0

这是我的解决方案

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.