如果在Ruby中使用File类时目录不存在,我该如何创建目录?

147

我有这个语句:

File.open(some_path, 'w+') { |f| f.write(builder.to_html)  }

何时使用?

some_path = "somedir/some_subdir/some-file.html"

我希望的是,如果路径中不存在名为somedirsome_subdir的目录(或两者皆无),则自动创建它们。

我该如何实现?

7个回答

179

如果父目录不存在,您可以使用FileUtils递归创建它们:

require 'fileutils'

dirname = File.dirname(some_path)
unless File.directory?(dirname)
  FileUtils.mkdir_p(dirname)
end

编辑:这里提供了一个仅使用核心库的解决方案(重新实现轮子,不建议使用)

dirname = File.dirname(some_path)
tokens = dirname.split(/[\/\\]/) # don't forget the backslash for Windows! And to escape both "\" and "/"

1.upto(tokens.size) do |n|
  dir = tokens[0...n]
  Dir.mkdir(dir) unless Dir.exist?(dir)
end

5
FileUtils 标准库中的一部分:http://www.ruby-doc.org/stdlib-1.9.3/libdoc/fileutils/rdoc/FileUtils.html - Eureka
1
我在我的答案中添加了一个仅包含核心的解决方案:请注意,它本质上是重新实现了 FileUtils.mkdir_p(这是专门针对您的用例的方法)。 - Eureka
67
请注意,FileUtils#mkdir_p即使目录层次结构已经存在(它什么也不做),仍然可以工作,因此这个解决方案可以压缩成一个一行代码加一个require语句的形式:FileUtils.mkdir_p(File.dirname(some_path)) - Eureka
@Eureka - 我正在解决一个问题,在这个问题中,即使目录已经存在,我们仍然会收到“Errno:EEXIST(文件已经存在 - foo)”的错误。这是一个比较老的应用程序,可能是规格已经发生了改变。 - JosephK
2
@JosephK - 对我来说,这个(误导性的)EEXIST错误最终成为了权限问题。 - TomG
显示剩余2条评论

98

对于那些想要在目录不存在时创建目录的人,这里是简单的解决方案:

require 'fileutils'

FileUtils.mkdir_p 'dir_name'

基于 Eureka 的 评论


1
这是@Eureka的评论 - “请注意,即使目录层次结构已经存在(它仅仅不做任何操作),FileUtils#mkdir_p也可以工作,因此此解决方案可以压缩为一个单行代码加上一个require语句:FileUtils.mkdir_p(File.dirname(some_path))”。 - Darpan

27
directory_name = "name"
Dir.mkdir(directory_name) unless File.exists?(directory_name)

2
使用此方法可能会遇到竞态条件,即在File.exists?运行后但在Dir.mkdir执行之前可能会创建目录。 - Matt Fenelon

8
使用Pathname怎么样?
require 'pathname'
some_path = Pathname("somedir/some_subdir/some-file.html")
some_path.dirname.mkdir_p
some_path.write(builder.to_html)

2
它使用some_path.dirname.mkpath而不是some_path.dirname.mkdir_p - Mauro Nidola
2
mkpath 上加上 +1。如果你只有目录而不是路径,就不需要 dirname,例如 Pathname("somedir/some_subdir").mkpath 将以相同的方式工作。 - Michael

7
根据他人的回答,什么也没有发生(不起作用)。没有错误提示,也没有创建目录。
以下是我需要执行的步骤:
require 'fileutils'
response = FileUtils.mkdir_p('dir_name')

我需要创建一个变量来捕获FileUtils.mkdir_p('dir_name')返回的响应...然后一切都很顺利!


没有意义。为什么你需要捕获返回值? - Tim Kretschmer
@huanson,我不需要捕获返回值...但是在我创建了response = FileUtils.mkdir_p('dir_name')之前,逻辑并没有起作用。如果我没有创建这个变量,FileUtils.mkdir_p('dir_name')对我来说就无法工作...或者至少我记得发生了这种情况(这个答案已经超过1年了)。如果Ruby的新版本修复了这个问题,我也不会感到惊讶。 - skplunkerin

1

在类似的情况下(并且取决于您的结构),这是我们解决存储截图位置的方法:

在我们的环境设置中(env.rb)

screenshotfolder = "./screenshots/#{Time.new.strftime("%Y%m%d%H%M%S")}"
unless File.directory?(screenshotfolder)
  FileUtils.mkdir_p(screenshotfolder)
end
Before do
  @screenshotfolder = screenshotfolder
  ...
end

在我们的 hooks.rb 文件中:

  screenshotName = "#{@screenshotfolder}/failed-#{scenario_object.title.gsub(/\s+/,"_")}-#{Time.new.strftime("%Y%m%d%H%M%S")}_screenshot.png";
  @browser.take_screenshot(screenshotName) if scenario.failed?

  embed(screenshotName, "image/png", "SCREENSHOT") if scenario.failed?

1

最佳答案中的“核心库”解决方案是不完整的。如果您只想使用核心库,请使用以下内容:

target_dir = ""

Dir.glob("/#{File.join("**", "path/to/parent_of_some_dir")}") do |folder|
  target_dir = "#{File.expand_path(folder)}/somedir/some_subdir/"
end

# Splits name into pieces
tokens = target_dir.split(/\//)

# Start at '/'
new_dir = '/'

# Iterate over array of directory names
1.upto(tokens.size - 1) do |n|

  # Builds directory path one folder at a time from top to bottom
  unless n == (tokens.size - 1)
    new_dir << "#{tokens[n].to_s}/" # All folders except innermost folder
  else
    new_dir << "#{tokens[n].to_s}" # Innermost folder
  end

  # Creates directory as long as it doesn't already exist
  Dir.mkdir(new_dir) unless Dir.exist?(new_dir)
end

我需要这个解决方案,因为FileUtils的依赖库rmagick阻止了我的Rails应用在Amazon Web Services上部署,因为rmagick依赖于libmagickwand-dev包(Ubuntu)/ imagemagick(OSX)才能正常工作。


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