Ruft mit Ruby die Namen aller Dateien aus einem Ordner ab

358

Ich möchte mit Ruby alle Dateinamen aus einem Ordner abrufen.

Željko Filipin
quelle

Antworten:

537

Sie haben auch die Verknüpfungsoption von

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

und wenn Sie alle Ruby-Dateien in einem Ordner oder Unterordner finden möchten:

Dir["/path/to/search/**/*.rb"]
Ian Eccles
quelle
5
Oder Sie können das gleiche mit Dir :: glob ()
Yoann Le Touche
2
Verwenden Sie auch ./...eher als~/
Minh Triet
5
Warum wird das bevorzugt?
BvuRVKyUVlViVIc7
1
@MinhTriet was macht das? Was ist vorzuziehen?
Stephenmurdoch
9
@marflar - ./bedeutet das aktuelle Verzeichnis, während /der Root-Mount-Punkt und ~/das Home-Verzeichnis des Benutzers ist. Wenn Sie das gesamte Projekt an einen anderen Ort verschieben, funktioniert das erste, die beiden anderen wahrscheinlich nicht.
Mirichan
170
Dir.entries(folder)

Beispiel:

Dir.entries(".")

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

Željko Filipin
quelle
15
Es sieht so aus, als würde er SO verwenden, um die Antworten auf Fragen zu dokumentieren, die er gerade gestellt hat. Eine Art Memo, nehme ich an. Daran kann man nicht viel auszusetzen sehen - obwohl dies ein wenig unvollständig ist ( Dir#globkönnte zum Beispiel vielleicht erwähnt worden sein), hindert nichts andere daran, eine wirklich gute Antwort zu veröffentlichen. Natürlich bin ich meistens ein Typ, der "halb voll" ist ...
Mike Woodhouse
1
@ Mike: Im großen Stil ist es wahrscheinlich keine große Sache. Und wie Sie sagen, wenn die Fragen und Antworten gut waren, könnte dies insgesamt ein Plus für die Website sein. Aber hier sind sowohl Frage als auch Antwort so minimal, dass es nicht besonders nützlich erscheint.
Telemachos
17
@Telemachus Ich benutze es Dirselten und jedes Mal, wenn ich es brauche, muss ich die Dokumentation lesen. Ich habe meine Frage und Antwort hier gepostet, damit ich sie später finden und vielleicht sogar jemandem mit der gleichen Frage helfen kann. Ich glaube, ich habe bei SO Podcast gehört, dass an einem solchen Verhalten nichts auszusetzen ist. Wenn Sie eine bessere Antwort haben, posten Sie diese bitte. Ich habe gepostet, was ich weiß, ich bin kein Ruby-Ninja. Ich akzeptiere regelmäßig Antworten mit den meisten Stimmen.
Željko Filipin
Dies kann eine bessere Option sein als Dir[]oder Dir.globwenn das Argument eine Variable ist. Wenn path = '/tmp', zu vergleichen: Dir.glob("#{path}/*")vs Dir.entries(path). Die Rückgabewerte unterscheiden sich geringfügig (".", ".."), letzteres ist jedoch auf einen Blick leichter zu erfassen.
Benjamin Oakes
92

Die folgenden Schnipsel genau zeigen den Namen der Dateien in einem Verzeichnis, das Überspringen Verzeichnisse und ".", ".."gepunkteter Ordner:

Dir.entries("your/folder").select {|f| !File.directory? f}
Emiliano Poggi
quelle
19
Kann auch ...select {|f| File.file? f}für eine klarere Bedeutung und kürzere Syntax tun .
Automatico
2
@squixy Hast du es richtig geschrieben?:Dir.entries("your/folder").select {|f| File.file? f}
Automatico
9
Ja. !File.directory?funktioniert aber File.file?nicht.
Kamil Lelonek
2
@squixy Ich hatte das gleiche Problem, in meinem Fall muss ich den vollständigen Pfad angeben, nicht nur den Dateinamen, der von Dir.foreach
TheLukeMcCarthy
6
.reject {|f| File.directory? f}scheint sauberer als .select{|f| !File.directory? f}. Oh, und jetzt sehe ich den ersten Kommentar ... auch gut.
Ian
36

So rekursiv alle Dateien (ausschließlich Dateien) abrufen:

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

Oder alles, was kein Verzeichnis ist ( File.file?würde nicht reguläre Dateien ablehnen):

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

Alternative Lösung

Die Verwendung Find#findeiner musterbasierten Suchmethode wie Dir.globist eigentlich besser. Siehe diese Antwort auf "Einzeiler zum rekursiven Auflisten von Verzeichnissen in Ruby?" .

konsolebox
quelle
18

Das funktioniert bei mir:

Wenn Sie keine versteckten Dateien [1] möchten, verwenden Sie 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 }

Jetzt gibt Dir.entries versteckte Dateien zurück, und Sie benötigen keinen Platzhalter-Stern (Sie können nur die Variable mit dem Verzeichnisnamen übergeben), sondern den Basisnamen direkt zurück, sodass die Funktionen von File.xxx nicht funktionieren .

# 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] Unter .dotfileUnix weiß ich nichts über Windows


quelle
9

Persönlich fand ich dies am nützlichsten, um Dateien in einem Ordner zu durchlaufen, vorausschauende Sicherheit:

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

Dies ist eine Lösung, um Dateien in einem Verzeichnis zu finden:

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

Während alle Dateinamen in einem Verzeichnis abgerufen werden, kann dieses Snippet verwendet werden, um sowohl Verzeichnisse [ ., ..] als auch versteckte Dateien abzulehnen , die mit a beginnen.

files = Dir.entries("your/folder").reject {|f| File.directory?(f) || f[0].include?('.')}
Lahiru
quelle
Dir.entriesGibt lokale Dateinamen zurück, keine absoluten Dateipfade. Erwartet andererseits File.directory?einen absoluten Dateipfad. Dieser Code funktioniert nicht wie erwartet.
Nathan
Es ist seltsam, dass der Code in Ihrem Fall nicht funktioniert. Da dies ein Code ist, den ich in einer Live-App verwendet habe, funktioniert das einwandfrei. Ich werde meinen Code erneut überprüfen und hier posten, wenn etwas in meinem ursprünglichen
Arbeitscode
1
@ Nathan Siehe meine Antwort für eine Erklärung
5

Dieser Code gibt nur Dateinamen mit ihrer Erweiterung zurück (ohne globalen Pfad).

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

Das funktioniert bei mir:

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

Dir.entriesGibt ein Array von Zeichenfolgen zurück. Dann müssen wir einen vollständigen Pfad der Datei zu File.file?angeben, es dirsei denn, dies entspricht unserem aktuellen Arbeitsverzeichnis. Deshalb das File.join().

yegor256
quelle
1
Sie müssen "." Ausschließen. und ".." aus Einträgen
Edgar Ortega
3

Möglicherweise möchten Sie auch Folgendes verwenden Rake::FileList(vorausgesetzt, Sie haben eine rakeAbhängigkeit):

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

Laut API:

Dateilisten sind faul. Wenn eine Liste von Glob-Mustern für mögliche Dateien angegeben wird, die in die Dateiliste aufgenommen werden sollen, enthält eine Dateiliste das Muster für die letztere Verwendung, anstatt die Dateistrukturen zu durchsuchen, um die Dateien zu finden.

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

Artur Beljajev
quelle
1

Wenn Sie ein Array von Dateinamen einschließlich Symlinks erhalten möchten , verwenden Sie

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

oder auch

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

und wenn Sie ohne Symlinks auskommen möchten , verwenden Sie

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

Verwenden Sie, wie in anderen Antworten gezeigt, Dir.glob('/path/to/dir/**/*')anstelle von, Dir.new('/path/to/dir')wenn Sie alle Dateien rekursiv abrufen möchten.

Mikhail Vasin
quelle
Oder verwenden Sie einfach*.*
Richard Peck
1
Dir.new('/home/user/foldername').each { |file| puts file }
Ashwin
quelle
1

Zusätzlich zu den Vorschlägen in diesem Thread wollte ich erwähnen, dass Sie, wenn Sie auch Punktdateien (.gitignore usw.) zurückgeben müssen, bei Dir.glob ein Flag wie folgt einfügen müssen: Dir.glob("/path/to/dir/*", File::FNM_DOTMATCH) Standardmäßig Dir.entries Enthält Punktedateien sowie aktuelle übergeordnete Verzeichnisse.

Für alle Interessierten war ich neugierig, wie die Antworten hier im Vergleich zur Ausführungszeit miteinander verglichen wurden, hier waren die Ergebnisse gegen tief verschachtelte Hierarchien. Die ersten drei Ergebnisse sind nicht rekursiv:

       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)

Diese wurden mit dem folgenden Benchmarking-Skript generiert:

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

Die Unterschiede in der Dir.entriesAnzahl der Dateien sind darauf zurückzuführen, dass standardmäßig ausgeblendete Dateien enthalten sind. Dir.entriesIn diesem Fall dauerte es etwas länger, da der absolute Pfad der Datei neu erstellt werden musste, um festzustellen, ob eine Datei ein Verzeichnis war, aber auch ohne dies dauerte es im rekursiven Fall immer noch länger als die anderen Optionen. Dies alles verwendete Ruby 2.5.1 unter OSX.

Ben Pennell
quelle
1

Ein einfacher Weg könnte sein:

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

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

Gibt die relativen Pfade der Datei aus dem Verzeichnis und allen Unterverzeichnissen zurück

Punksta
quelle
0

In einem IRB-Kontext können Sie Folgendes verwenden, um die Dateien im aktuellen Verzeichnis abzurufen:

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

Sie können diese Funktion auch für andere Verzeichnisse verwenden:

file_names = `ls ~/Documents`.split("\n")
Balaji Radhakrishnan
quelle
Diese Lösung hat bei mir funktioniert, da ich eine Legacy-Lösung mit einer alten Ruby-Version habe, die den Befehl
Dir.children