Como crio um diretório se não existe nenhum usando a classe File no Ruby?

121

Eu tenho esta afirmação:

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

Onde

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

O que eu quero acontecer é, se não houver diretório chamado somedirou some_subdirou ambos no caminho, eu quero que criá-lo automagicamente.

Como eu posso fazer isso?

marcamillion
fonte

Respostas:

154

Você pode usar o FileUtils para criar diretórios pai recursivamente, se eles ainda não estiverem presentes:

require 'fileutils'

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

Edit: Aqui está uma solução usando apenas as bibliotecas principais (reimplementar a roda, não recomendado)

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
Eureka
fonte
Ah ok. Eu quis dizer o núcleo, não o stdlib. De qualquer maneira, tudo bem. Isso funciona. Obrigado!
Marcamillion 27/09/12
1
I adicionada uma solução única-core para a minha resposta: Esteja ciente, no entanto, que essencialmente reimplementa FileUtils.mkdir_p(que é o método dedicado ao seu caso de uso)
Eureka
57
Observe que FileUtils#mkdir_pfunciona mesmo que a hierarquia de diretórios já exista (ela simplesmente não faz nada); portanto, esta solução pode ser compactada neste one-liner da lata, além de uma instrução de exigência:FileUtils.mkdir_p(File.dirname(some_path))
Eureka
1
@ JosephphK - para mim, esse erro (enganoso) EEXIST acabou sendo um problema de permissão.
TomG
81

Para quem procura uma maneira de criar um diretório, se ele não existir , aqui está a solução simples:

require 'fileutils'

FileUtils.mkdir_p 'dir_name'

Baseado no comentário de Eureka .

Andrey Mikhaylov - lolmaus
fonte
1
Este é o comentário de @ Eureka - "Observe que o FileUtils # mkdir_p funciona mesmo que a hierarquia de diretórios já exista (apenas não faz nada), portanto, esta solução pode ser compactada nesse liner de uma lata, além de uma declaração de exigência: FileUtils.mkdir_p(File.dirname(some_path))"
Darpan
23
directory_name = "name"
Dir.mkdir(directory_name) unless File.exists?(directory_name)
Licysca
fonte
2
Você pode encontrar condições de corrida usando esse método. O diretório pode ser criado após o File.exists? é executado, mas antes que o Dir.mkdir seja executado.
Matt Fenelon
4

Com base nas respostas dos outros, nada aconteceu (não funcionou). Não houve erro e nenhum diretório criado.

Aqui está o que eu precisava fazer:

require 'fileutils'
response = FileUtils.mkdir_p('dir_name')

Eu precisava criar uma variável para pegar a resposta que FileUtils.mkdir_p('dir_name')envia de volta ... então tudo funcionou como um encanto!

skplunkerin
fonte
não faz sentido. por que você precisa pegar o retorno?
Tim Kretschmer
@ Huanson, eu não precisava pegar o retorno ... mas a lógica não funcionou até que eu criei response = FileUtils.mkdir_p('dir_name'). Se eu não criei essa variável, FileUtils.mkdir_p('dir_name')não estava funcionando para mim ... ou pelo menos é o que eu lembro que aconteceu (esta resposta tem mais de um ano). Não ficaria surpreso se uma versão mais recente do Ruby resolver esse problema.
skplunkerin
2

Que tal usar Pathname?

require 'pathname'
some_path = Pathname("somedir/some_subdir/some-file.html")
some_path.dirname.mkdir_p
some_path.write(builder.to_html)
ferros e areias
fonte
1
Trabalha com em some_path.dirname.mkpathvez desome_path.dirname.mkdir_p
Mauro Nidola 12/03/19
1
+1 ativado mkpath. Além disso, se você tiver apenas o diretório e não o caminho, não há necessidade dirname, por exemplo, Nome do caminho ("somedir / some_subdir"). Mkpath funcionará da mesma maneira.
Michael
1

Seguindo linhas semelhantes (e dependendo da sua estrutura), é assim que resolvemos onde armazenar as capturas de tela:

Em nossa configuração de env (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

E nos nossos 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?
Shell Bryson
fonte
1

A única solução da "biblioteca principal" da resposta principal estava incompleta. Se você deseja usar apenas as bibliotecas principais, use o seguinte:

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

Eu precisava dessa solução porque o gemmag de dependência do FileUtils impediu que meu aplicativo Rails fosse implantado no Amazon Web Services, pois o rmagick depende do pacote libmagickwand-dev (Ubuntu) / imagemagick (OSX) para funcionar corretamente.

CopyLeft
fonte