Iterar a través de cada archivo en un directorio


274

¿Cómo escribo un bucle en ruby ​​para poder ejecutar un bloque de código en cada archivo?

Soy nuevo en Ruby, y he concluido que la forma de hacerlo es hacer cada ciclo.
El archivo ruby ​​se ejecutará desde un directorio diferente al directorio que quiero recorrer.

Lo intenté Dir.foreachy no pude hacerlo funcionar.


2
¿Puedes especificar qué sucedió cuando intentaste que funcionara? ¿Qué código exacto probaste (o el fragmento relevante, si es largo)? ¿Qué mensajes de error recibiste? Dir.foreachfunciona para iterar sobre el contenido de un directorio, por lo que algo más está sucediendo.
Telémaco, el

3
Si solo desea archivos dentro de su directorio, no olvide probar los archivos al iterar sobre el contenido del directorio: do_something_with(entry) if File.file?(entry)
glenn jackman

3
Úselo 'img/*.{jpg,png,gif,jpeg}'para tomar múltiples extensiones.
Benjamin Crouzier

Desafortunadamente, @ChrisPeters parece poco probable, ya que el OP no ha estado en el sitio en más de cuatro años.
Joe Kennedy

Respuestas:


430

Como han dicho otros, Dir::foreaches una buena opción aquí. Sin embargo, tenga en cuenta que Dir::foreachy Dir::entriessiempre incluirá .y ..(los directorios actual y principal). Por lo general, no querrá trabajar en ellos, por lo que puede usar Dir::each_childo Dir::children(como lo sugiere ma11hew28 ) o hacer algo como esto:

Dir.foreach('/path/to/dir') do |filename|
  next if filename == '.' or filename == '..'
  # Do work on the remaining files & directories
end

Dir::foreachy Dir::entries(así como Dir::each_childy Dir::children) también incluyen archivos y directorios ocultos. A menudo, esto es lo que desea, pero si no lo es, debe hacer algo para saltearlos.

Alternativamente, es posible que desee investigar Dir::globqué proporciona una coincidencia de comodines simple:

Dir.glob('/path/to/dir/*.rb') do |rb_filename|
  # Do work on files & directories ending in .rb
end

12
¡Úselo Dir.foreachsi el directorio contiene una gran cantidad de archivos!
Tilo

55
¡Gracias! Pequeño mod para hacerlo aún mejor:next if File.directory? item
mr.buttons

@ mr.buttons Eso no siempre hará lo correcto. A veces la gente quiere trabajar tanto en directorios como en archivos. Di código para evitar las listas especiales para .o ..porque la gente casi siempre quiere ignorar esos dos.
Telémaco

3
@Tilo: solo por interés, ¿quieres explicar con un poco más de detalle por qué? :)
mkataja

11
@mkataja Dir.foreachitera en lugar de construir una matriz (potencialmente enorme) por adelantado (lo que Dir.globhace). Entonces, si el directorio es realmente enorme, puede hacer una diferencia de rendimiento. En circunstancias normales no lo notarías, pero en condiciones de estrés, puede ser absolutamente importante.
Telémaco

99

Este es mi método favorito para ser fácil de leer:

Dir.glob("*/*.txt") do |my_text_file|
  puts "working on: #{my_text_file}..."
end

E incluso puede extender esto para trabajar en todos los archivos en subdirectorios:

Dir.glob("**/*.txt") do |my_text_file| # note one extra "*"
  puts "working on: #{my_text_file}..."
end

30

Dir también tiene una sintaxis más corta para obtener una matriz de todos los archivos del directorio:

Dir['dir/to/files/*'].each do |fname|
    # do something with fname
end

¿Qué en este código impide que los directorios también se usen con fnamelas iteraciones?
kayleeFrye_onDeck

26
Dir.foreach("/home/mydir") do |fname|
  puts fname
end

2
Alternativamente use Dir # [] o Dir # glob
Ryan Bigg

13

La biblioteca de búsqueda está diseñada específicamente para esta tarea: https://ruby-doc.org/stdlib-2.5.1/libdoc/find/rdoc/Find.html

require 'find'
Find.find(path) do |file|
  # process
end

Esta es una biblioteca estándar de ruby, por lo que debería estar disponible


1
File.findbaja recursivamente tanto como sea posible, comenzando por cualquier camino que le des. No estoy seguro de que eso sea lo que quiere el OP.
Telémaco

Parece que no tengo acceso a ese método: ¿Find.find? ¿Necesito descargar una biblioteca para incluir esta funcionalidad?
cielo azul

@ user470184: "Buscar" es una biblioteca ruby ​​estándar y debe estar disponible con la instalación ruby ​​predeterminada. Sin embargo, debe "requerir 'buscar'" antes de poder usarlo.
Faisal

1
@Faisal ¿Puedo pasar patrones globales como *.rbafind()
Ashhar Hasan

7

Me gusta este, que no se ha mencionado anteriormente.

require 'pathname'

Pathname.new('/my/dir').children.each do |path|
    puts path
end

El beneficio es que obtienes un objeto Pathname en lugar de una cadena, con el que puedes hacer cosas útiles y recorrer más.


3
Dir.new('/my/dir').each do |name|
  ...
end

1
Además de Dir.new ('/ my / dir') también hay Dir.entries ('/ my / dir') pero Dir.foreach () es un poco más sucinto.
The Tin Man

55
@ZED También Dir.foreachitera mientras Dir.entriesconstruye toda la matriz a la vez. Entonces, si el directorio es inmenso, es menos un golpe de memoria. (No suele ser un gran problema, probablemente, pero aún así ...)
Telemachus

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.