Obter nomes de todos os arquivos de uma pasta com Ruby

358

Quero obter todos os nomes de arquivos de uma pasta usando Ruby.

Željko Filipin
fonte

Respostas:

538

Você também tem a opção de atalho de

Dir["/path/to/search/*"]

e se você deseja encontrar todos os arquivos Ruby em qualquer pasta ou subpasta:

Dir["/path/to/search/**/*.rb"]
Ian Eccles
fonte
5
Ou você pode fazer o mesmo com Dir :: glob ()
Yoann Le Touche
2
Além disso, use ./...ao invés de~/
Minh Triet
5
Por que isso é preferido?
BvuRVKyUVlViVIc7
11
@MinhTriet, o que isso faz? O que é preferível?
21320
9
@marflar - ./significa o diretório atual, enquanto que /é o ponto de montagem raiz e ~/o diretório inicial do usuário. Se você mover o projeto inteiro para outro lugar, o primeiro funcionará, mas os outros dois provavelmente não.
mirichan
170
Dir.entries(folder)

exemplo:

Dir.entries(".")

Fonte: http://ruby-doc.org/core/classes/Dir.html#method-c-entries

Željko Filipin
fonte
15
Parece que ele está usando o SO para documentar as respostas às perguntas que acabou de fazer. Uma espécie de memorando, suponho. Não dá para ver muita coisa errada com isso - afinal, mesmo que este seja um pouco incompleto ( Dir#globpoderia ter sido mencionado, por exemplo), não há nada para impedir que outra pessoa poste uma resposta realmente boa. 'Claro, eu sou mais um 'copo meio cheio' espécie de um cara ...
Mike Woodhouse
11
@ Mike: No grande esquema das coisas, provavelmente não é grande coisa. E, como você diz, se as perguntas e respostas forem boas, pode ser uma vantagem geral para o site. Mas aqui a pergunta e a resposta são tão mínimas que não parecem ser especialmente úteis.
Telemachus
17
@Telemachus eu uso Dirraramente, e toda vez que preciso preciso ler a documentação. Postei minha pergunta e resposta aqui para encontrá-la mais tarde e talvez até ajudar alguém com a mesma pergunta. Acho que ouvi no podcast da SO que não há nada de errado com esse comportamento. Se você tiver uma resposta melhor, poste-a. Publiquei o que sei, não sou um ninja Ruby. Aceito regularmente respostas com mais votos.
Željko Filipin
Essa pode ser uma opção melhor do que Dir[]ou Dir.globquando o argumento é uma variável. Quando path = '/tmp', compare: Dir.glob("#{path}/*")vs Dir.entries(path). Os valores de retorno são um pouco diferentes (".", ".."), mas o último é mais fácil de entender rapidamente.
Benjamin Oakes
92

Os seguintes trechos mostra exatamente o nome dos arquivos dentro de um diretório, subdiretórios pular e ".", ".."pastas pontilhadas:

Dir.entries("your/folder").select {|f| !File.directory? f}
Emiliano Poggi
fonte
19
Também pode ser útil ...select {|f| File.file? f}para obter um significado mais claro e uma sintaxe mais curta.
Automatico
2
@squixy Você escreveu corretamente ?:Dir.entries("your/folder").select {|f| File.file? f}
Automatico
9
Sim. !File.directory?está funcionando, mas File.file?não está.
Kamil Lelonek
2
@squixy Eu tive o mesmo problema, no meu caso eu preciso para fornecer o caminho completo e não apenas o nome do arquivo retornado por Dir.foreach
TheLukeMcCarthy
6
.reject {|f| File.directory? f}parece mais limpo do que .select{|f| !File.directory? f}. Ah, e agora eu vejo o primeiro comentário ... também é bom.
26515 Ian
36

Para obter todos os arquivos (somente arquivos estritamente) recursivamente:

Dir.glob('path/**/*').select{ |e| File.file? e }

Ou qualquer coisa que não seja um diretório ( File.file?rejeitaria arquivos não regulares):

Dir.glob('path/**/*').reject{ |e| File.directory? e }

Solução alternativa

Usar Find#findum método de pesquisa baseado em padrões como Dir.globé realmente melhor. Veja esta resposta em "Lista única para listar recursivamente diretórios em Ruby?" .

konsolebox
fonte
18

Isso funciona para mim:

Se você não deseja arquivos ocultos [1], use Dir [] :

# With a relative path, Dir[] will return relative paths 
# as `[ './myfile', ... ]`
#
Dir[ './*' ].select{ |f| File.file? f } 

# Want just the filename?
# as: [ 'myfile', ... ]
#
Dir[ '../*' ].select{ |f| File.file? f }.map{ |f| File.basename f }

# Turn them into absolute paths?
# [ '/path/to/myfile', ... ]
#
Dir[ '../*' ].select{ |f| File.file? f }.map{ |f| File.absolute_path f }

# With an absolute path, Dir[] will return absolute paths:
# as: [ '/home/../home/test/myfile', ... ]
#
Dir[ '/home/../home/test/*' ].select{ |f| File.file? f }

# Need the paths to be canonical?
# as: [ '/home/test/myfile', ... ]
#
Dir[ '/home/../home/test/*' ].select{ |f| File.file? f }.map{ |f| File.expand_path f }

Agora, Dir.entries retornará arquivos ocultos e você não precisará do curinga asterix (basta passar a variável com o nome do diretório), mas retornará o nome da base diretamente, para que as funções File.xxx não funcionem .

# In the current working dir:
#
Dir.entries( '.' ).select{ |f| File.file? f }

# In another directory, relative or otherwise, you need to transform the path 
# so it is either absolute, or relative to the current working dir to call File.xxx functions:
#
home = "/home/test"
Dir.entries( home ).select{ |f| File.file? File.join( home, f ) }

[1] .dotfileno unix, eu não sei sobre o Windows


fonte
14

Agora, no Ruby 2.5, você pode usar Dir.children. Ele obtém nomes de arquivos como uma matriz, exceto por "." e ".."

Exemplo:

Dir.children("testdir")   #=> ["config.h", "main.rb"]

http://ruby-doc.org/core-2.5.0/Dir.html#method-c-children

Mario Pérez
fonte
9

Pessoalmente, achei isso o mais útil para fazer loop sobre arquivos em uma pasta, com segurança no futuro:

Dir['/etc/path/*'].each do |file_name|
  next if File.directory? file_name 
end
mr.buttons
fonte
9

Esta é uma solução para encontrar arquivos em um diretório:

files = Dir["/work/myfolder/**/*.txt"]

files.each do |file_name|
  if !File.directory? file_name
    puts file_name
    File.open(file_name) do |file|
      file.each_line do |line|
        if line =~ /banco1/
          puts "Found: #{line}"
        end
      end
    end
  end
end
gilcierweb
fonte
6

Ao obter todos os nomes de arquivo em um diretório, esse trecho pode ser usado para rejeitar os diretórios [ ., ..] e os arquivos ocultos que começam com um.

files = Dir.entries("your/folder").reject {|f| File.directory?(f) || f[0].include?('.')}
Lahiru
fonte
Dir.entriesretorna nomes de arquivos locais, não caminhos de arquivo absolutos. Por outro lado, File.directory?espera um caminho de arquivo absoluto. Este código não funciona conforme o esperado.
Nathan
É estranho que o código não funcione no seu caso. Como esse é um código que eu usei em um aplicativo ao vivo que funciona muito bem. Vou verificar novamente meu código e postar aqui se há alguma coisa faltando em meu código de trabalho original :)
Lahiru
11
@Nathan Veja minha resposta para uma explicação
5

esse código retorna apenas nomes de arquivos com sua extensão (sem um caminho global)

Dir.children("/path/to/search/")
Игорь Хлебников
fonte
4

Isto é o que funciona para mim:

Dir.entries(dir).select { |f| File.file?(File.join(dir, f)) }

Dir.entriesretorna uma matriz de seqüências de caracteres. Então, temos que fornecer um caminho completo do arquivo para File.file?, a menos que dirseja igual ao nosso diretório de trabalho atual. É por isso que isso File.join().

yegor256
fonte
11
Você precisa excluir "." e ".." das entradas
Edgar Ortega
3

Você também pode querer usar Rake::FileList(desde que tenha rakedependência):

FileList.new('lib/*') do |file|
  p file
end

De acordo com a API:

As listas de arquivos são preguiçosas. Quando é fornecida uma lista de padrões globais para a inclusão de possíveis arquivos na lista de arquivos, em vez de procurar nas estruturas de arquivos para encontrar os arquivos, um FileList mantém o padrão para uso posterior.

https://docs.ruby-lang.org/en/2.1.0/Rake/FileList.html

Artur Beljajev
fonte
1

Se você deseja obter uma variedade de nomes de arquivos, incluindo links simbólicos , use

Dir.new('/path/to/dir').entries.reject { |f| File.directory? f }

ou mesmo

Dir.new('/path/to/dir').reject { |f| File.directory? f }

e se você quiser ficar sem links simbólicos , use

Dir.new('/path/to/dir').select { |f| File.file? f }

Conforme mostrado em outras respostas, use em Dir.glob('/path/to/dir/**/*')vez de Dir.new('/path/to/dir')se desejar obter todos os arquivos recursivamente.

Mikhail Vasin
fonte
Ou apenas use*.*
Richard Peck
1
Dir.new('/home/user/foldername').each { |file| puts file }
Ashwin
fonte
1

Além das sugestões neste tópico, eu gostaria de mencionar que, se você também precisar retornar arquivos de ponto (.gitignore, etc), com o Dir.glob, será necessário incluir um sinalizador da seguinte maneira: Dir.glob("/path/to/dir/*", File::FNM_DOTMATCH) Por padrão, Dir.entries inclui arquivos de ponto, bem como os diretórios pai atuais.

Para qualquer pessoa interessada, fiquei curioso para saber como as respostas aqui se comparavam em tempo de execução, eis os resultados contra uma hierarquia profundamente aninhada. Os três primeiros resultados são não recursivos:

       user     system      total        real
Dir[*]: (34900 files stepped over 100 iterations)
  0.110729   0.139060   0.249789 (  0.249961)
Dir.glob(*): (34900 files stepped over 100 iterations)
  0.112104   0.142498   0.254602 (  0.254902)
Dir.entries(): (35600 files stepped over 100 iterations)
  0.142441   0.149306   0.291747 (  0.291998)
Dir[**/*]: (2211600 files stepped over 100 iterations)
  9.399860  15.802976  25.202836 ( 25.250166)
Dir.glob(**/*): (2211600 files stepped over 100 iterations)
  9.335318  15.657782  24.993100 ( 25.006243)
Dir.entries() recursive walk: (2705500 files stepped over 100 iterations)
 14.653018  18.602017  33.255035 ( 33.268056)
Dir.glob(**/*, File::FNM_DOTMATCH): (2705500 files stepped over 100 iterations)
 12.178823  19.577409  31.756232 ( 31.767093)

Eles foram gerados com o seguinte script de benchmarking:

require 'benchmark'
base_dir = "/path/to/dir/"
n = 100
Benchmark.bm do |x|
  x.report("Dir[*]:") do
    i = 0
    n.times do
      i = i + Dir["#{base_dir}*"].select {|f| !File.directory? f}.length
    end
    puts " (#{i} files stepped over #{n} iterations)"
  end
  x.report("Dir.glob(*):") do
    i = 0
    n.times do
      i = i + Dir.glob("#{base_dir}/*").select {|f| !File.directory? f}.length
    end
    puts " (#{i} files stepped over #{n} iterations)"
  end
  x.report("Dir.entries():") do
    i = 0
    n.times do
      i = i + Dir.entries(base_dir).select {|f| !File.directory? File.join(base_dir, f)}.length
    end
    puts " (#{i} files stepped over #{n} iterations)"
  end
  x.report("Dir[**/*]:") do
    i = 0
    n.times do
      i = i + Dir["#{base_dir}**/*"].select {|f| !File.directory? f}.length
    end
    puts " (#{i} files stepped over #{n} iterations)"
  end
  x.report("Dir.glob(**/*):") do
    i = 0
    n.times do
      i = i + Dir.glob("#{base_dir}**/*").select {|f| !File.directory? f}.length
    end
    puts " (#{i} files stepped over #{n} iterations)"
  end
  x.report("Dir.entries() recursive walk:") do
    i = 0
    n.times do
      def walk_dir(dir, result)
        Dir.entries(dir).each do |file|
          next if file == ".." || file == "."

          path = File.join(dir, file)
          if Dir.exist?(path)
            walk_dir(path, result)
          else
            result << file
          end
        end
      end
      result = Array.new
      walk_dir(base_dir, result)
      i = i + result.length
    end
    puts " (#{i} files stepped over #{n} iterations)"
  end
  x.report("Dir.glob(**/*, File::FNM_DOTMATCH):") do
    i = 0
    n.times do
      i = i + Dir.glob("#{base_dir}**/*", File::FNM_DOTMATCH).select {|f| !File.directory? f}.length
    end
    puts " (#{i} files stepped over #{n} iterations)"
  end
end

As diferenças nas contagens de arquivos são devidas à Dir.entriesinclusão de arquivos ocultos por padrão. Dir.entriesacabou demorando um pouco mais nesse caso, devido à necessidade de reconstruir o caminho absoluto do arquivo para determinar se um arquivo era um diretório, mas mesmo assim ele ainda estava demorando consistentemente mais do que as outras opções no caso recursivo. Isso tudo estava usando o ruby ​​2.5.1 no OSX.

Ben Pennell
fonte
1

Uma maneira simples pode ser:

dir = './' # desired directory
files = Dir.glob(File.join(dir, '**', '*')).select{|file| File.file?(file)}

files.each do |f|
    puts f
end
Sebastian Capone
fonte
0
def get_path_content(dir)
  queue = Queue.new
  result = []
  queue << dir
  until queue.empty?
    current = queue.pop
    Dir.entries(current).each { |file|
      full_name = File.join(current, file)
      if not (File.directory? full_name)
        result << full_name
      elsif file != '.' and file != '..'
          queue << full_name
      end
    }
  end
  result
end

retorna os caminhos relativos do arquivo do diretório e de todos os subdiretórios

punksta
fonte
0

Em um contexto de IRB, você pode usar o seguinte para obter os arquivos no diretório atual:

file_names = `ls`.split("\n")

Você também pode fazer isso funcionar em outros diretórios:

file_names = `ls ~/Documents`.split("\n")
Balaji Radhakrishnan
fonte
Esta solução funcionou para mim desde que eu tenho uma solução legado com versão antiga de rubi que não suporta comandos Dir.children
Ciprian Dragoe