Однолінійний рекурсивний список каталогів у Ruby?


96

Який найшвидший, найбільш оптимізований однолінійний спосіб отримати масив каталогів (крім файлів) у Ruby?

Як щодо включення файлів?


2
Найшвидший, оптимізований і однолінійний лайнер може суперечити читабельному / ремонтопридатному. І ви могли б дізнатись це за допомогою тесту та швидкого тестування.
Олов'яна людина

Відповіді:


180
Dir.glob("**/*/") # for directories
Dir.glob("**/*") # for all files

Замість цього Dir.glob(foo)ви також можете писати Dir[foo](однак Dir.globтакож можете взяти блок, і в цьому випадку він отримає кожен шлях замість створення масиву).

Документи Ruby Glob


10
використовуйте, Dir.glob("**/")якщо ви також не хочете посилань
johannes

6
А як щодо прихованих файлів та каталогів?
аледіаферія

6
Щоб включити точкові файли до результатів матчу, використовуйте File::FNM_DOTMATCHпрапор.
x-yuri

2
Дякую @ x-yuri! Прапор btw вказаний так:Dir.glob("**/*", File::FNM_DOTMATCH)
vlz


53

Я вважаю, що жодне з вирішень тут не стосується прихованих каталогів (наприклад, '.test'):

require 'find'
Find.find('.') { |e| puts e if File.directory?(e) }

Find.find('/tmp').collect {|_|_}Мені було корисно
Alexander Bird

Будь обережні з Знахідкою, це не слід симлінк: stackoverflow.com/questions/3974087 / ...
leebutts

31

Список каталогів спробуйте

Dir['**/']

Список файлів складніше, оскільки в каталозі Unix також є файл, тому вам потрібно перевірити тип або видалити записи з повернутого списку, який є батьківським для інших записів.

Dir['**/*'].reject {|fn| File.directory?(fn) }

А для списку всіх файлів і каталогів просто

Dir['**/*']

Зауважте, що він сказав "включати також файли", а не "лише файли", тому вам не потрібно видаляти каталоги.
sepp2k 03.03.10

1
@ sepp2k Так, я пропустив цю частину, коли грав у irb. Але я залишаю це тут на випадок, якщо хтось може шукати щось подібне :-)
MBO

7

Швидкий один лайнер

Тільки каталоги

`find -type d`.split("\n")

Каталоги та звичайні файли

`find -type d -or -type f`.split("\n")`

Чистий гарний рубін

require "pathname"

def rec_path(path, file= false)
  puts path
  path.children.collect do |child|
    if file and child.file?
      child
    elsif child.directory?
      rec_path(child, file) + [child]
    end
  end.select { |x| x }.flatten(1)
end

# only directories
rec_path(Pathname.new(dir), false)
# directories and normal files
rec_path(Pathname.new(dir), true)

1
Помилковий: Dir.glob ("# {DIRECTORY} / ** / * /"). Map {| каталог | Pathname.new (каталог)}
Роберт Росс,

Хтось може пояснити end.select {}.flatten()частину? Мені подобається функція в цілому. Схоже, це створить масив масивів? Чи можна було б виконати elseifчастину за допомогою: rec_path(child, file) << child.to_sщоб ви могли призначити її масиву та отримати масив рядків? Дякую!
MCP

7

Як зазначено в інших відповідях тут, ви можете використовувати Dir.glob. Майте на увазі, що в папках може бути багато дивних символів, а аргументи глобуса є зразками, тому деякі символи мають особливі значення. Таким чином, небезпечно робити щось на зразок наступного:

Dir.glob("#{folder}/**/*")

Замість цього робіть:

Dir.chdir(folder) { Dir.glob("**/*").map {|path| File.expand_path(path) } }

2

У PHP або інших мовах, щоб отримати вміст каталогу та всіх його підкаталогів, потрібно написати рядки коду, але в Ruby це потрібно 2 рядки:

require 'find'
Find.find('./') do |f| p f end

це надрукує вміст поточного каталогу та всіх його підкаталогів.

Або коротше: Ви можете використовувати ’**’позначення:

p Dir['**/*.*']

Скільки рядків ви напишете на PHP або на Java, щоб отримати однаковий результат?


13
Проголосовано за пряме копіювання з mustap.com/rubyzone_post_162_recursive-directory-listing без посилання на джерело ...
eckza,

@kivetros Я відредагував відповідь, щоб включити до архіву версію посилання :-)
onebree

0

Хоча це не однолінійне рішення, я думаю, що це найкращий спосіб зробити це за допомогою рубінних дзвінків.

Спочатку видаліть усі файли рекурсивно
Друге видаліть усі порожні каталоги

Dir.glob("./logs/**/*").each { |file| File.delete(file) if File.file? file }
Dir.glob("./logs/**/*/").each { |directory| Dir.delete(directory) }

3
Він / вона не хоче видаляти файли / каталоги.
Darek Nędza

Як це зробити для обох файлів + каталогу в одному рядку?
vipin8169

0

Ось приклад, що поєднує динамічне відкриття каталогу проекту Rails з Dir.glob:

dir = Dir.glob(Rails.root.join('app', 'assets', 'stylesheets', '*'))

Я спробував це >> config.assets.paths << Rails.root.join("app", "assets", "*"), але все ще не міг побачити підпапки та файли у папці активівRails.application.config.assets.paths
vipin8169

-1
Dir.open(Dir.pwd).map { |h| (File.file?(h) ? "#{h} - file" : "#{h} - folder") if h[0] != '.' }

крапки повертають нуль, використовуйте компактні


1
Це не надто вишукано, і для того, щоб бути таким, потрібно поставити ще декілька рядків
onebree
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.