Obtenez les noms de tous les fichiers d'un dossier avec Ruby


Réponses:


538

Vous avez également l'option de raccourci

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

et si vous souhaitez trouver tous les fichiers Ruby dans n'importe quel dossier ou sous-dossier:

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

5
Ou vous pouvez faire la même chose avec Dir :: glob ()
Yoann Le Touche

2
Aussi, utilisez ./...plutôt que~/
Minh Triet

5
Pourquoi est-ce préférable?
BvuRVKyUVlViVIc7

1
@MinhTriet qu'est-ce que cela fait? Qu'est-ce que c'est préférable?
stephenmurdoch le

9
@marflar - ./signifie le répertoire courant, alors que /c'est le point de montage racine, et ~/est le répertoire personnel de l'utilisateur. Si vous déplacez l'ensemble du projet ailleurs, le premier fonctionnera, mais les deux autres ne le seront probablement pas.
mirichan

170
Dir.entries(folder)

exemple:

Dir.entries(".")

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


15
On dirait qu'il utilise SO pour documenter les réponses aux questions qu'il vient de lui poser. Une sorte de mémo, je suppose. Je ne vois pas grand-chose de mal à cela - après tout, même si celui-ci est un peu incomplet ( Dir#globaurait peut-être pu être mentionné, par exemple), rien n'empêche quelqu'un d'autre de poster une très bonne réponse. Bien sûr, je suis surtout une sorte de "verre à moitié plein" ...
Mike Woodhouse

1
@ Mike: Dans le grand schéma des choses, ce n'est probablement pas un gros problème. Et comme vous le dites, si les questions et réponses étaient bonnes, cela pourrait être globalement un plus pour le site. Mais ici, la question et la réponse sont si minimes que cela ne semble pas particulièrement utile.
Télémaque

17
@Telemachus J'utilise Dirrarement, et chaque fois que j'en ai besoin, je dois lire la documentation. J'ai posté ma question et ma réponse ici pour que je puisse la retrouver plus tard, et peut-être même aider quelqu'un avec la même question. Je pense avoir entendu au podcast SO qu'il n'y a rien de mal à un tel comportement. Si vous avez une meilleure réponse, veuillez la poster. J'ai posté ce que je sais, je ne suis pas un ninja Ruby. J'accepte régulièrement les réponses avec le plus de votes.
Željko Filipin

Cela peut être une meilleure option que Dir[]ou Dir.globlorsque l'argument est une variable. Lorsque path = '/tmp', comparez: Dir.glob("#{path}/*")vs Dir.entries(path). Les valeurs de retour sont légèrement différentes (".", ".."), mais ce dernier est plus facile à cerner d'un coup d'œil.
Benjamin Oakes

92

Les extraits suivants montrent exactement le nom des fichiers dans un répertoire, et sauter les sous - répertoires ".", ".."dossiers en pointillés:

Dir.entries("your/folder").select {|f| !File.directory? f}

19
Peut également faire ...select {|f| File.file? f}pour une signification plus claire et une syntaxe plus courte.
Automatico

2
@squixy Avez-vous bien écrit?:Dir.entries("your/folder").select {|f| File.file? f}
Automatico

9
Oui. !File.directory?fonctionne mais File.file?pas.
Kamil Lelonek

2
@squixy J'ai eu le même problème, dans mon cas, je dois fournir le chemin complet et pas seulement le nom de fichier renvoyé par Dir.foreach
TheLukeMcCarthy

6
.reject {|f| File.directory? f}semble plus propre que .select{|f| !File.directory? f}. Oh, et maintenant je vois le premier commentaire ... aussi bien.
Ian

36

Pour obtenir tous les fichiers (strictement les fichiers uniquement) de manière récursive:

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

Ou tout ce qui n'est pas un répertoire ( File.file?rejetterait les fichiers non réguliers):

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

Solution alternative

Il est préférable d' utiliser Find#findune méthode de recherche basée sur un modèle comme Dir.glob. Voir cette réponse à "One-liner pour répertorier récursivement les répertoires dans Ruby?" .


18

Cela fonctionne pour moi:

Si vous ne voulez pas de fichiers cachés [1], utilisez 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 }

Maintenant, Dir.entries renverra des fichiers cachés, et vous n'avez pas besoin de l'astérisme générique (vous pouvez simplement passer la variable avec le nom du répertoire), mais il renverra directement le nom de base, donc les fonctions File.xxx ne fonctionneront pas .

# 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] .dotfilesous unix, je ne connais pas Windows



9

Personnellement, j'ai trouvé cela le plus utile pour boucler sur des fichiers dans un dossier, pour une sécurité prospective:

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

9

Ceci est une solution pour rechercher des fichiers dans un répertoire:

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

6

Tout en obtenant tous les noms de fichiers dans un répertoire, cet extrait peut être utilisé pour rejeter à la fois les répertoires [ ., ..] et les fichiers cachés qui commencent par un.

files = Dir.entries("your/folder").reject {|f| File.directory?(f) || f[0].include?('.')}

Dir.entriesrenvoie des noms de fichiers locaux, pas des chemins de fichiers absolus. D'un autre côté, File.directory?attend un chemin de fichier absolu. Ce code ne fonctionne pas comme prévu.
Nathan

C'est bizarre que le code ne fonctionne pas dans votre cas. Comme c'est un code que j'ai utilisé dans une application en direct qui fonctionne très bien. Je revérifierai mon code et publierai ici s'il manque quelque chose dans mon code de travail d'origine :)
Lahiru

1
@Nathan Voir ma réponse pour une explication


4

C'est ce qui fonctionne pour moi:

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

Dir.entriesrenvoie un tableau de chaînes. Ensuite, nous devons fournir un chemin complet du fichier vers File.file?, à moins qu'il ne dirsoit égal à notre répertoire de travail actuel. Voilà pourquoi ça File.join().


1
Vous devez exclure "." et ".." des entrées
Edgar Ortega

3

Vous pouvez également utiliser Rake::FileList(à condition que vous ayez une rakedépendance):

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

Selon l'API:

Les listes de fichiers sont paresseuses. Lorsqu'on lui donne une liste de modèles glob pour les fichiers possibles à inclure dans la liste de fichiers, au lieu de rechercher les structures de fichiers pour trouver les fichiers, une FileList contient le modèle pour une utilisation ultérieure.

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


1

Si vous souhaitez obtenir un tableau de noms de fichiers, y compris des liens symboliques , utilisez

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

ou même

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

et si vous voulez vous passer de liens symboliques , utilisez

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

Comme indiqué dans d'autres réponses, utilisez Dir.glob('/path/to/dir/**/*')plutôt que Dir.new('/path/to/dir')si vous souhaitez obtenir tous les fichiers de manière récursive.


Ou utilisez simplement*.*
Richard Peck

1
Dir.new('/home/user/foldername').each { |file| puts file }

1

En plus des suggestions de ce fil, je voulais mentionner que si vous devez également renvoyer des fichiers dot (.gitignore, etc.), avec Dir.glob, vous devrez inclure un indicateur comme Dir.glob("/path/to/dir/*", File::FNM_DOTMATCH) suit : Par défaut, Dir.entries inclut des fichiers de points, ainsi que les répertoires parents actuels.

Pour toute personne intéressée, j'étais curieux de voir comment les réponses ici se comparaient les unes aux autres en temps d'exécution, voici les résultats contre une hiérarchie profondément imbriquée. Les trois premiers résultats sont non récursifs:

       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)

Ceux-ci ont été générés avec le script d'analyse comparative suivant:

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

Les différences dans le nombre de fichiers sont dues à l' Dir.entriesinclusion de fichiers cachés par défaut. Dir.entriesa fini par prendre un peu plus de temps dans ce cas en raison de la nécessité de reconstruire le chemin absolu du fichier pour déterminer si un fichier était un répertoire, mais même sans cela, cela prenait toujours plus de temps que les autres options du cas récursif. Tout cela utilisait ruby ​​2.5.1 sur OSX.


1

Une façon simple pourrait être:

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

files.each do |f|
    puts f
end

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

renvoie les chemins relatifs du fichier depuis le répertoire et tous les sous-répertoires


0

Dans un contexte IRB, vous pouvez utiliser ce qui suit pour obtenir les fichiers dans le répertoire actuel:

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

Vous pouvez également faire fonctionner cela sur d'autres répertoires:

file_names = `ls ~/Documents`.split("\n")

Ces solutions ont fonctionné pour moi car j'ai une solution héritée avec une ancienne version ruby ​​qui ne prend pas en charge la commande Dir.children
Ciprian Dragoe
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.