Jak utworzyć katalog, jeśli nie istnieje, używając klasy File w Rubim?

123

Mam to oświadczenie:

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

Gdzie

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

Co chcę się zdarzyć, jeśli nie istnieje katalog o nazwie somedirlub some_subdirczy zarówno w ścieżce, chcę go automagicznie go utworzyć.

Jak mogę to zrobić?

marcamillion
źródło

Odpowiedzi:

155

Możesz użyć FileUtils do rekurencyjnego tworzenia katalogów nadrzędnych, jeśli jeszcze nie istnieją:

require 'fileutils'

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

Edycja: Oto rozwiązanie wykorzystujące tylko podstawowe biblioteki (ponowna implementacja koła, niezalecane)

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
źródło
4
FileUtils znajduje się w standardowym katalogu: ruby-doc.org/stdlib-1.9.3/libdoc/fileutils/rdoc/FileUtils.html
Eureka
OK. Miałem na myśli rdzeń, a nie standardową bibliotekę. Tak czy inaczej, w porządku. To działa. Dzięki!
marcamillion
1
Dodałem do mojej odpowiedzi tylko podstawowe rozwiązanie: pamiętaj jednak, że zasadniczo reimplementuje FileUtils.mkdir_p(co jest metodą dedykowaną dla twojego przypadku użycia)
Eureka
57
Zauważ, że FileUtils#mkdir_pdziała, nawet jeśli hierarchia katalogów już istnieje (po prostu nic nie robi), więc to rozwiązanie można skompresować do tego można w jednym linijce plus wymaganie:FileUtils.mkdir_p(File.dirname(some_path))
Eureka
1
@JosephK - dla mnie ten (mylący) błąd EEXIST skończył się kwestią pozwolenia.
TomG
82

Dla tych, którzy szukają sposobu na utworzenie katalogu, jeśli nie istnieje , oto proste rozwiązanie:

require 'fileutils'

FileUtils.mkdir_p 'dir_name'

Na podstawie komentarza Eureki .

Andrey Mikhaylov - lolmaus
źródło
1
To jest komentarz @ Eureka - „Zauważ, że FileUtils # mkdir_p działa, nawet jeśli hierarchia katalogów już istnieje (po prostu nic nie robi), więc to rozwiązanie można skompresować do tej puszki z jednym FileUtils.mkdir_p(File.dirname(some_path))
wierszem
23
directory_name = "name"
Dir.mkdir(directory_name) unless File.exists?(directory_name)
Licysca
źródło
2
Możesz napotkać warunki wyścigu używając tej metody, katalog może zostać utworzony po tym, jak File.exists? działa, ale przed wykonaniem Dir.mkdir.
Matt Fenelon
4

Na podstawie odpowiedzi innych osób nic się nie stało (nie zadziałało). Nie było błędu i nie utworzono katalogu.

Oto, co musiałem zrobić:

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

Musiałem stworzyć zmienną, aby złapać odpowiedź, która FileUtils.mkdir_p('dir_name')odsyła ... wtedy wszystko działało jak urok!

skplunkerin
źródło
nie ma sensu. dlaczego musisz złapać zwrot?
Tim Kretschmer
@huanson, nie musiałem łapać powrotu ... ale logika nie działała, dopóki nie stworzyłem response = FileUtils.mkdir_p('dir_name'). Gdybym nie utworzył tej zmiennej, FileUtils.mkdir_p('dir_name')nie działałby dla mnie ... a przynajmniej tak się stało (ta odpowiedź ma więcej niż 1 rok). Nie zdziwiłbym się, gdyby nowsza wersja Rubiego rozwiązała ten problem.
skplunkerin
2

A co powiesz na używanie Pathname?

require 'pathname'
some_path = Pathname("somedir/some_subdir/some-file.html")
some_path.dirname.mkdir_p
some_path.write(builder.to_html)
ironsand
źródło
1
Działa z some_path.dirname.mkpathzamiastsome_path.dirname.mkdir_p
Mauro Nidola
1
+1 włączone mkpath. Również jeśli masz tylko katalog, a nie ścieżkę, nie ma potrzeby, aby dirnamenp. Ścieżka („katalog / jakiś_podkatalog”). Mkpath będzie działać w ten sam sposób.
Michael
1

W podobny sposób (iw zależności od struktury) rozwiązaliśmy, gdzie przechowywać zrzuty ekranu:

W naszej konfiguracji środowiska (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

A w naszym 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
źródło
1

Jedyne rozwiązanie dotyczące „biblioteki podstawowej” w pierwszej odpowiedzi było niekompletne. Jeśli chcesz używać tylko podstawowych bibliotek, użyj następujących:

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

Potrzebowałem tego rozwiązania, ponieważ rmagick zależności FileUtils uniemożliwił wdrożenie mojej aplikacji Rails w Amazon Web Services, ponieważ rmagick zależy od pakietu libmagickwand-dev (Ubuntu) / imagemagick (OSX), aby działał poprawnie.

CopyLeft
źródło