Obtenga nombres de todos los archivos de una carpeta con Ruby

Respuestas:

538

También tiene la opción de acceso directo de

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

y si desea encontrar todos los archivos Ruby en cualquier carpeta o subcarpeta:

Dir["/path/to/search/**/*.rb"]
Ian Eccles
fuente
55
O puede hacer lo mismo con Dir :: glob ()
Yoann Le Touche
2
Además, use en ./...lugar de~/
Minh Triet
55
¿Por qué se prefiere esto?
BvuRVKyUVlViVIc7
1
@MinhTriet, ¿qué hace eso? ¿Qué es preferible?
stephenmurdoch
99
@marflar: ./significa el directorio actual, mientras que /es el punto de montaje raíz y ~/es el directorio de inicio del usuario. Si mueve todo el proyecto a otro lugar, el primero funcionará, pero los otros dos probablemente no.
mirichan
170
Dir.entries(folder)

ejemplo:

Dir.entries(".")

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

Željko Filipin
fuente
15
Parece que está usando SO para documentar las respuestas a las preguntas que acaba de formular. Una especie de memo, supongo. No puedo ver mucho mal con eso, después de todo, a pesar de que este está un poco incompleto ( Dir#globtal vez podría haber sido mencionado, por ejemplo), no hay nada que evite que alguien más publique una Respuesta realmente buena. 'Por supuesto, soy sobre todo un 'vaso medio lleno' una especie de chico ...
Mike Woodhouse
1
@ Mike: En el gran esquema de las cosas, probablemente no sea gran cosa. Y como usted dice si las preguntas y respuestas fueron buenas, podría ser una ventaja general para el sitio. Pero aquí, tanto la pregunta como la respuesta son tan mínimas que no parece especialmente útil.
Telémaco
17
@Telemachus que uso Dirraramente, y cada vez que lo necesito tengo que leer la documentación. He publicado mi pregunta y respuesta aquí para poder encontrarla más tarde, y tal vez incluso ayudar a alguien con la misma pregunta. Creo que he escuchado en el podcast SO que no hay nada de malo en este comportamiento. Si tiene una mejor respuesta, publíquela. He publicado lo que sé, no soy un ninja Ruby. Regularmente acepto respuestas con la mayoría de los votos.
Željko Filipin
Esta puede ser una mejor opción que Dir[]o Dir.globcuando el argumento es una variable. Cuando path = '/tmp', comparar: Dir.glob("#{path}/*")vs Dir.entries(path). Los valores de retorno son ligeramente diferentes (".", ".."), pero este último es más fácil de asimilar a simple vista.
Benjamin Oakes
92

Los siguientes fragmentos de muestra exactamente el nombre de los archivos dentro de un directorio, subdirectorios y saltar ".", ".."carpetas de puntos:

Dir.entries("your/folder").select {|f| !File.directory? f}
Emiliano Poggi
fuente
19
También puede hacerlo ...select {|f| File.file? f}para un significado más claro y una sintaxis más corta.
Automático el
2
@squixy ¿Lo escribiste correctamente ?Dir.entries("your/folder").select {|f| File.file? f}
Automatico
99
Sí. !File.directory?está funcionando pero File.file?no.
Kamil Lelonek
2
@squixy Tuve el mismo problema, en mi caso necesito proporcionar la ruta completa, no solo el nombre de archivo devuelto por Dir.foreach
TheLukeMcCarthy
66
.reject {|f| File.directory? f}parece más limpio que .select{|f| !File.directory? f}. Ah, y ahora veo el primer comentario ... también bueno.
Ian
36

Para obtener todos los archivos (estrictamente solo archivos) de forma recursiva:

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

O cualquier cosa que no sea un directorio ( File.file?rechazaría archivos no regulares):

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

Solución alternativa

Usar Find#findmás de un método de búsqueda basado en patrones como en Dir.globrealidad es mejor. Vea esta respuesta a "One-liner para enumerar directorios recursivamente en Ruby? .

konsolebox
fuente
18

Esto funciona para mi:

Si no desea archivos 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 }

Ahora, Dir.entries devolverá archivos ocultos, y no necesita el asterisco comodín (solo puede pasar la variable con el nombre del directorio), pero devolverá el nombre base directamente, por lo que las funciones File.xxx no funcionarán .

# 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] .dotfileen Unix, no sé sobre Windows


fuente
9

Personalmente, encontré que esto es lo más útil para recorrer los archivos en una carpeta, con miras a la seguridad:

Dir['/etc/path/*'].each do |file_name|
  next if File.directory? file_name 
end
Sr. botones
fuente
9

Esta es una solución para buscar archivos en un directorio:

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
fuente
6

Al obtener todos los nombres de archivo en un directorio, este fragmento se puede utilizar para rechazar los directorios [ ., ..] y los archivos ocultos que comienzan con un.

files = Dir.entries("your/folder").reject {|f| File.directory?(f) || f[0].include?('.')}
Lahiru
fuente
Dir.entriesdevuelve nombres de archivos locales, no rutas de archivos absolutas. Por otro lado, File.directory?espera una ruta de archivo absoluta. Este código no funciona como se esperaba.
Nathan
Es extraño que el código no funcione en tu caso. Como este es un código que he usado en una aplicación en vivo que funciona bien. Revisaré mi código y publicaré aquí si falta algo de mi código de trabajo original :)
Lahiru
1
@Nathan Vea mi respuesta para una explicación
5

este código solo devuelve nombres de archivo con su extensión (sin una ruta global)

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

Esto es lo que funciona para mí:

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

Dir.entriesdevuelve una matriz de cadenas. Luego, tenemos que proporcionar una ruta completa del archivo File.file?, a menos que dirsea ​​igual a nuestro directorio de trabajo actual. Por eso esto File.join().

yegor256
fuente
1
Debe excluir "." y ".." de las entradas
Edgar Ortega
3

También es posible que desee utilizar Rake::FileList(siempre que tenga rakedependencia):

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

De acuerdo con la API:

Las listas de archivos son flojas. Cuando se le proporciona una lista de patrones globales para posibles archivos que se incluirán en la lista de archivos, en lugar de buscar en las estructuras de archivos para encontrar los archivos, una FileList contiene el patrón para su uso posterior.

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

Artur Beljajev
fuente
1

Si desea obtener una variedad de nombres de archivos, incluidos enlaces simbólicos , use

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

o incluso

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

y si quieres ir sin enlaces simbólicos , usa

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

Como se muestra en otras respuestas, utilice en Dir.glob('/path/to/dir/**/*')lugar de Dir.new('/path/to/dir')si desea obtener todos los archivos de forma recursiva.

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

Además de las sugerencias en este hilo, quería mencionar que si también necesita devolver archivos de puntos (.gitignore, etc.), con Dir.glob necesitaría incluir un indicador como tal: Dir.glob("/path/to/dir/*", File::FNM_DOTMATCH) De manera predeterminada, las entradas de Dir. incluye archivos de puntos, así como los directorios principales actuales.

Para cualquier persona interesada, tenía curiosidad de cómo las respuestas aquí se comparaban entre sí en tiempo de ejecución, aquí estaban los resultados contra una jerarquía profundamente anidada. Los primeros tres resultados no son 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)

Estos se generaron con el siguiente script de evaluación comparativa:

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

Las diferencias en el recuento de archivos se deben a la Dir.entriesinclusión de archivos ocultos de forma predeterminada. Dir.entriesterminó tomando un poco más de tiempo en este caso debido a la necesidad de reconstruir la ruta absoluta del archivo para determinar si un archivo era un directorio, pero incluso sin eso todavía tardaba mucho más que las otras opciones en el caso recursivo. Todo esto estaba usando ruby ​​2.5.1 en OSX.

Ben Pennell
fuente
1

Una forma simple podría ser:

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

files.each do |f|
    puts f
end
Sebastian Capone
fuente
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

devuelve las rutas relativas del archivo desde el directorio y todos los subdirectorios

punksta
fuente
0

En un contexto IRB, puede usar lo siguiente para obtener los archivos en el directorio actual:

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

También puede hacer que esto funcione en otros directorios:

file_names = `ls ~/Documents`.split("\n")
Balaji Radhakrishnan
fuente
Esta solución funcionó para mí ya que tengo una solución heredada con una versión antigua de ruby ​​que no admite el comando Dir.children
Ciprian Dragoe