Itérer à travers chaque fichier dans un répertoire

274

Comment écrire une boucle en ruby ​​pour pouvoir exécuter un bloc de code sur chaque fichier?

Je suis nouveau sur ruby ​​et j'ai conclu que la façon de le faire est de faire chaque boucle.
Le fichier ruby ​​sera exécuté à partir d'un répertoire différent de celui que je souhaite parcourir.

J'ai essayé le Dir.foreach et je n'ai pas réussi à le faire fonctionner.

Andrew
la source
2
Pouvez-vous préciser ce qui s'est passé lorsque vous avez essayé de le faire fonctionner? Quel code exact avez-vous essayé (ou le morceau correspondant, s'il est long)? Quels messages d'erreur avez-vous reçus? Dir.foreachfonctionne pour itérer sur le contenu d'un répertoire, donc quelque chose d'autre se passe.
Télémaque
3
Si vous ne voulez que des fichiers dans votre répertoire, n'oubliez pas de tester les fichiers lors de l'itération sur le contenu du répertoire: do_something_with(entry) if File.file?(entry)
glenn jackman
3
Utilisez 'img/*.{jpg,png,gif,jpeg}'pour saisir plusieurs extensions.
Benjamin Crouzier
@ChrisPeters semble malheureusement peu probable, car l'OP n'a pas été sur le site depuis plus de quatre ans.
Joe Kennedy

Réponses:

430

Comme d'autres l'ont dit, Dir::foreachc'est une bonne option ici. Cependant, notez que Dir::foreachet Dir::entriesinclura toujours .et ..(les répertoires courant et parent). Vous ne voudrez généralement pas travailler dessus, vous pouvez donc utiliser Dir::each_childou Dir::children(comme suggéré par ma11hew28 ) ou faire quelque chose comme ceci:

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

Dir::foreachet Dir::entries(ainsi que Dir::each_childetDir::children ) incluent également des fichiers et répertoires cachés. C'est souvent ce que vous voulez, mais si ce n'est pas le cas, vous devez faire quelque chose pour les ignorer.

Alternativement, vous voudrez peut-être examiner Dir::globce qui fournit une correspondance simple avec des caractères génériques:

Dir.glob('/path/to/dir/*.rb') do |rb_filename|
  # Do work on files & directories ending in .rb
end
Télémaque
la source
12
À utiliser Dir.foreachsi le répertoire contient un grand nombre de fichiers!
Tilo
5
Merci! Petit mod pour le rendre encore meilleur:next if File.directory? item
mr.buttons
@ mr.buttons Cela ne fera pas toujours la bonne chose. Parfois, les gens veulent travailler sur des répertoires ainsi que sur des fichiers. J'ai donné un code pour éviter les listes spéciales pour .ou ..parce que les gens veulent presque toujours ignorer ces deux-là.
Télémaque
3
@Tilo: juste par intérêt, attention à expliquer un peu plus en détail pourquoi? :)
mkataja
11
@mkataja Dir.foreachitère plutôt que de créer un tableau (potentiellement énorme) à l'avant (ce qui est le Dir.globcas). Donc, si le répertoire est vraiment énorme, il peut faire une différence de performance. Dans des circonstances normales, vous ne le remarqueriez pas, mais dans des conditions de stress, cela peut absolument avoir de l'importance.
Télémaque le
99

C'est ma méthode préférée pour être facile à lire:

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

Et vous pouvez même l'étendre pour qu'il fonctionne sur tous les fichiers des sous-répertoires:

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

Dir a également une syntaxe plus courte pour obtenir un tableau de tous les fichiers du répertoire:

Dir['dir/to/files/*'].each do |fname|
    # do something with fname
end
wawka
la source
Qu'est-ce qui, dans ce code, empêche les répertoires d'être également utilisés avec fnameles itérations de?
kayleeFrye_onDeck
26
Dir.foreach("/home/mydir") do |fname|
  puts fname
end
Fred
la source
2
Vous pouvez également utiliser Dir # [] ou Dir # glob
Ryan Bigg
13

La bibliothèque de recherche est conçue spécifiquement pour cette tâche: https://ruby-doc.org/stdlib-2.5.1/libdoc/find/rdoc/Find.html

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

Ceci est une bibliothèque rubis standard, elle devrait donc être disponible

Faisal
la source
1
File.finddescend récursivement aussi loin que possible, en commençant par le chemin que vous lui donnez. Je ne suis pas sûr que ce soit ce que souhaite le PO.
Télémaque
Je ne semble pas avoir accès à cette méthode - Find.find? Dois-je télécharger une bibliothèque ou inclure cette fonctionnalité?
blue-sky
@ user470184: "Find" est une bibliothèque ruby ​​standard et devrait être disponible avec l'installation ruby ​​par défaut. Cependant, vous devez "exiger" trouver "" avant de pouvoir l'utiliser.
Faisal
1
@Faisal Puis - je transmettre des modèles glob comme *.rbàfind()
Ashhar Hasan
7

J'aime celui-ci, qui n'a pas été mentionné ci-dessus.

require 'pathname'

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

L'avantage est que vous obtenez un objet Pathname au lieu d'une chaîne, que vous pouvez faire des choses utiles avec et parcourir plus loin.

skagedal
la source
3
Dir.new('/my/dir').each do |name|
  ...
end
Nick Moore
la source
1
En plus de Dir.new ('/ my / dir'), il y a aussi Dir.entries ('/ my / dir') mais Dir.foreach () est un peu plus succinct.
le Tin Man
5
@ZED Itère également Dir.foreachtout en Dir.entriesconstruisant le tableau entier à la fois. Donc, si le répertoire est immense, c'est moins un coup de mémoire. (Ce n'est généralement pas un gros problème, probablement, mais quand même ...)
Télémaque
2

Pour ignorer .& .., vous pouvez utiliser Dir::each_child.

Dir.each_child('/path/to/dir') do |filename|
  puts filename
end

Dir::children renvoie un tableau des noms de fichiers.

ma11hew28
la source