One-liner para listar diretórios recursivamente em Ruby?

96

Qual é a maneira mais rápida e otimizada de uma linha para obter um array de diretórios (excluindo arquivos) em Ruby?

Que tal incluir arquivos?

Lance Pollard
fonte
2
Mais rápido, mais otimizado e de uma linha pode estar em conflito com legível / sustentável. E você pode descobrir isso usando um benchmark e um teste rápido.
The Tin Man de

Respostas:

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

Em vez de, Dir.glob(foo)você também pode escrever Dir[foo](no entanto, Dir.globtambém pode pegar um bloco, caso em que produzirá cada caminho em vez de criar uma matriz).

Ruby Glob Docs

sepp2k
fonte
10
use a Dir.glob("**/")menos que você também queira links simbólicos
johannes,
6
E quanto a arquivos e diretórios ocultos?
alediaferia
6
Para incluir dotfiles nos resultados da partida, use o File::FNM_DOTMATCHsinalizador.
x-yuri
2
Obrigado @ x-yuri! O sinalizador btw é especificado assim:Dir.glob("**/*", File::FNM_DOTMATCH)
vlz
2
Aqui está a documentação: ruby-doc.org/core-2.2.0/Dir.html#method-c-glob
stephen.hanson
53

Acredito que nenhuma das soluções aqui lida com diretórios ocultos (por exemplo, '.test'):

require 'find'
Find.find('.') { |e| puts e if File.directory?(e) }
FelipeC
fonte
Find.find('/tmp').collect {|_|_}Foi útil para mim
Alexander Bird
Tenha cuidado com Find, ele não segue links simbólicos: stackoverflow.com/questions/3974087/…
leebutts
31

Para uma lista de diretórios, tente

Dir['**/']

Lista de arquivos é mais difícil, porque no diretório Unix também é um arquivo, então você precisa testar o tipo ou remover as entradas da lista retornada que é pai de outras entradas.

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

E para a lista de todos os arquivos e diretórios simplesmente

Dir['**/*']
MBO
fonte
Observe que ele disse "também incluir arquivos", não "apenas arquivos", portanto, você não precisa remover os diretórios.
sepp2k,
1
@ sepp2k Sim, eu perdi essa parte quando estava jogando com o irb. Mas deixo aqui para o caso de alguém pesquisar algo semelhante :-)
MBO
7

Fast one liner

Apenas diretórios

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

Diretórios e arquivos normais

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

Rubi lindo puro

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)
johannes
fonte
1
Falso: Dir.glob ("# {DIRETÓRIO} / ** / * /"). Map {| diretório | Pathname.new (diretório)}
Robert Ross
Alguém pode explicar a end.select {}.flatten()parte? Eu gosto da função geral. Parece que vai criar um array de arrays? Seria possível fazer a elseifparte com: rec_path(child, file) << child.to_spara que você pudesse atribuí-la a um array e obter um array de strings? Obrigado!
MCP
7

Conforme observado em outras respostas aqui, você pode usar Dir.glob. Lembre-se de que as pastas podem conter muitos caracteres estranhos e os argumentos glob são padrões, portanto, alguns caracteres têm significados especiais. Como tal, não é seguro fazer algo como o seguinte:

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

Em vez disso, faça:

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

Em PHP ou outras linguagens, para obter o conteúdo de um diretório e todos os seus subdiretórios, você deve escrever algumas linhas de código, mas em Ruby são necessárias 2 linhas:

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

isso imprimirá o conteúdo do diretório atual e todos os seus subdiretórios.

Ou mais curto, você pode usar a ’**’notação:

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

Quantas linhas você escreverá em PHP ou em Java para obter o mesmo resultado?

Ramesh
fonte
13
Votos negativos para copiar e colar diretamente de mustap.com/rubyzone_post_162_recursive-directory-listing sem citar a fonte ...
eckza
@kivetros Editei a resposta para incluir a versão arquivada do link :-)
onebree
0

Embora não seja uma solução de uma linha, acho que essa é a melhor maneira de fazer isso usando chamadas de ruby.

Primeiro apague todos os arquivos recursivamente
Segundo apague todos os diretórios vazios

Dir.glob("./logs/**/*").each { |file| File.delete(file) if File.file? file }
Dir.glob("./logs/**/*/").each { |directory| Dir.delete(directory) }
HomeTurf
fonte
3
Ele / ela não deseja excluir arquivos / diretórios.
Darek Nędza
Como fazer isso para os dois arquivos + diretório em uma única linha?
vipin8169
0

Aqui está um exemplo que combina a descoberta dinâmica de um diretório de projeto Rails com Dir.glob:

dir = Dir.glob(Rails.root.join('app', 'assets', 'stylesheets', '*'))
shacker
fonte
Eu tentei isso >> config.assets.paths << Rails.root.join("app", "assets", "*"), mas ainda não conseguia ver as subpastas e arquivos dentro da pasta de ativos porRails.application.config.assets.paths
vipin8169
-1
Dir.open(Dir.pwd).map { |h| (File.file?(h) ? "#{h} - file" : "#{h} - folder") if h[0] != '.' }

pontos retornam nulo, use compacto

ruvan
fonte
1
Isso não é muito elegante e precisaria estar em mais algumas linhas para ser assim
onebree