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

96

Каков самый быстрый, наиболее оптимизированный и однострочный способ получить массив каталогов (исключая файлы) в Ruby?

Как насчет включения файлов?

Лэнс Поллард
источник
2
Самая быстрая, наиболее оптимизированная и однострочная версия может противоречить читаемой / поддерживаемой. И вы можете узнать это с помощью теста производительности и быстрого тестирования.
Железный Человек

Ответы:

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

Вместо этого Dir.glob(foo)вы также можете написать Dir[foo](но Dir.globтакже можете взять блок, и в этом случае он будет выдавать каждый путь вместо создания массива).

Документы Ruby Glob

sepp2k
источник
10
используйте, Dir.glob("**/")если вам также не нужны символические ссылки
Йоханнес
6
А как насчет скрытых файлов и каталогов?
alediaferia
6
Чтобы включить точечные файлы в результаты поиска, используйте File::FNM_DOTMATCHфлаг.
x-yuri
2
Спасибо @ x-yuri! Флаг, кстати, указан так:Dir.glob("**/*", File::FNM_DOTMATCH)
vlz 05
2
Вот документация: ruby-doc.org/core-2.2.0/Dir.html#method-c-glob
stephen.hanson
53

Я считаю, что ни одно из решений здесь не касается скрытых каталогов (например, '.test'):

require 'find'
Find.find('.') { |e| puts e if File.directory?(e) }
FelipeC
источник
Find.find('/tmp').collect {|_|_}Был полезен для меня
Александр Бёрд
Будьте осторожны с Find, он не следует символическим
ссылкам
31

Для списка каталогов попробуйте

Dir['**/']

Список файлов сложнее, потому что в каталоге Unix также есть файл, поэтому вам нужно проверить тип или удалить записи из возвращенного списка, который является родительским для других записей.

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

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

Dir['**/*']
MBO
источник
Обратите внимание, что он сказал «также включать файлы», а не «только файлы», поэтому вам не нужно удалять каталоги.
sepp2k 03
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 {| directory | Pathname.new (directory)}
Роберт Росс
Кто-нибудь может объяснить эту end.select {}.flatten()часть? Мне нравится эта функция в целом. Похоже, что создадим массив массивов? Можно ли выполнить эту elseifчасть с помощью:, rec_path(child, file) << child.to_sчтобы вы могли назначить ее массиву и получить массив строк? Спасибо!
MCP
7

Как отмечено в других ответах здесь, вы можете использовать Dir.glob. Имейте в виду, что в папках может быть много странных символов, а аргументы glob являются шаблонами, поэтому некоторые символы имеют особое значение. Поэтому небезопасно делать что-то вроде следующего:

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

Вместо этого сделайте:

Dir.chdir(folder) { Dir.glob("**/*").map {|path| File.expand_path(path) } }
Troelskn
источник
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

Хотя это и не однострочное решение, я думаю, что это лучший способ сделать это с помощью вызовов ruby.

Сначала удалите все файлы рекурсивно.
Затем удалите все пустые каталоги.

Dir.glob("./logs/**/*").each { |file| File.delete(file) if File.file? file }
Dir.glob("./logs/**/*/").each { |directory| Dir.delete(directory) }
Главная
источник
3
Он / она не хочет удалять файлы / каталоги.
Дарек Нендза
Как это сделать для обоих файлов + каталога в одной строке?
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