Uzyskaj nazwy wszystkich plików z folderu za pomocą Ruby

358

Chcę uzyskać wszystkie nazwy plików z folderu za pomocą Ruby.

Željko Filipin
źródło

Odpowiedzi:

537

Masz również opcję skrótu

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

a jeśli chcesz znaleźć wszystkie pliki Ruby w dowolnym folderze lub podfolderze:

Dir["/path/to/search/**/*.rb"]
Ian Eccles
źródło
5
Lub możesz zrobić to samo z Dir :: glob ()
Yoann Le Touche,
2
Ponadto używaj ./...zamiast~/
Minh Triet
5
Dlaczego jest to preferowane?
BvuRVKyUVlViVIc7
1
@MinhTriet co to robi? Co jest lepsze?
stephenmurdoch
9
@marflar - ./oznacza bieżący katalog, podczas gdy /jest głównym punktem podłączenia i ~/jest katalogiem domowym użytkownika. Jeśli przeniesiesz cały projekt gdzieś indziej, pierwszy zadziała, ale dwa pozostałe prawdopodobnie nie.
mirichan
170
Dir.entries(folder)

przykład:

Dir.entries(".")

Źródło: http://ruby-doc.org/core/classes/Dir.html#method-c-entries

Željko Filipin
źródło
15
Wygląda na to, że używa SO do udokumentowania odpowiedzi na pytania, które właśnie zadał. Przypuszczam, że to rodzaj notatki. Nie widzę w tym nic złego - w końcu, mimo że ten jest trochę niekompletny ( Dir#globna przykład może być wspomniany), nic nie stoi na przeszkodzie, aby ktoś inny opublikował naprawdę dobrą odpowiedź. „Oczywiście, jestem typem faceta„ na wpół zapełnionego ”…
Mike Woodhouse
1
@Mike: W wielkim schemacie rzeczy prawdopodobnie nie jest to wielka sprawa. I jak powiesz, jeśli pytania i odpowiedzi byłyby dobre, może to być plus dla witryny. Ale tutaj zarówno pytanie, jak i odpowiedź są tak minimalne, że nie wydaje się szczególnie przydatne.
Telemachus,
17
@ Telemachus Używam Dirrzadko i za każdym razem, gdy go potrzebuję, muszę czytać dokumentację. Wysłałem tutaj moje pytanie i odpowiedź, aby móc je później znaleźć, a może nawet pomóc komuś z tym samym pytaniem. Myślę, że słyszałem na SO podcast, że nie ma nic złego w takim zachowaniu. Jeśli masz lepszą odpowiedź, opublikuj ją. Opublikowałem to, co wiem, nie jestem rubinowym ninja. Regularnie przyjmuję odpowiedzi z największą liczbą głosów.
Željko Filipin,
To może być lepszym rozwiązaniem niż Dir[]lub Dir.globgdy argumentem jest zmienna. Kiedy path = '/tmp', porównaj: Dir.glob("#{path}/*")vs Dir.entries(path). Zwracane wartości są nieco inne („.”, „..”), ale te ostatnie można łatwiej sprawdzić na pierwszy rzut oka.
Benjamin Oakes
92

Poniższe fragmenty dokładnie pokazuje nazwy plików wewnątrz katalogu, pomijanie i podkatalogów ".", ".."przerywanymi folderach:

Dir.entries("your/folder").select {|f| !File.directory? f}
Emiliano Poggi
źródło
19
Można to również zrobić ...select {|f| File.file? f}dla jaśniejszego znaczenia i krótszej składni.
Automatico,
2
@squixy Czy napisałeś to poprawnie ?:Dir.entries("your/folder").select {|f| File.file? f}
Automatico
9
Tak. !File.directory?działa, ale File.file?nie.
Kamil Lelonek
2
@squixy Miałem ten sam problem, w moim przypadku muszę podać pełną ścieżkę, a nie tylko nazwę pliku zwróconą przez Dir.foreach
TheLukeMcCarthy
6
.reject {|f| File.directory? f}wydaje się czystszy niż .select{|f| !File.directory? f}. Aha, a teraz widzę pierwszy komentarz ... również dobry.
Ian
36

Aby rekurencyjnie uzyskać wszystkie pliki (tylko pliki):

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

Lub coś, co nie jest katalogiem ( File.file?odrzuciłoby pliki nieregularne):

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

Alternatywne rozwiązanie

Używanie Find#findmetody wyszukiwania opartej na wzorcach jak Dir.globjest w rzeczywistości lepsze. Zobacz tę odpowiedź do „Jednowarstwowa lista rekurencyjnie wyświetlająca katalogi w Rubim?” .

konsolebox
źródło
18

To działa dla mnie:

Jeśli nie chcesz ukrytych plików [1], użyj 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 }

Teraz Dir.entries zwróci ukryte pliki i nie potrzebujesz wieloznacznej asterix (możesz po prostu przekazać zmienną z nazwą katalogu), ale zwróci ona bezpośrednio basename, więc funkcje File.xxx nie będą działać .

# 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] .dotfilena Uniksie, nie wiem o Windowsie


źródło
9

Osobiście uważam, że jest to najbardziej przydatne do zapętlania plików w folderze, patrząc w przyszłość:

Dir['/etc/path/*'].each do |file_name|
  next if File.directory? file_name 
end
przyciski mr
źródło
9

To jest rozwiązanie, aby znaleźć pliki w katalogu:

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
źródło
6

Podczas uzyskiwania wszystkich nazw plików w katalogu tego fragmentu można użyć do odrzucenia zarówno katalogów [ ., ..], jak i ukrytych plików, które zaczynają się od.

files = Dir.entries("your/folder").reject {|f| File.directory?(f) || f[0].include?('.')}
Lahiru
źródło
Dir.entrieszwraca lokalne nazwy plików, a nie bezwzględne ścieżki plików. Z drugiej strony File.directory?oczekuje bezwzględnej ścieżki do pliku. Ten kod nie działa zgodnie z oczekiwaniami.
Nathan
To dziwne, że kod nie działa w twoim przypadku. Ponieważ jest to kod, którego użyłem w aplikacji na żywo, która działa dobrze.
Ponownie sprawdzę
1
@Nathan Zobacz moją odpowiedź, aby uzyskać wyjaśnienie
5

ten kod zwraca tylko nazwy plików z ich rozszerzeniem (bez ścieżki globalnej)

Dir.children("/path/to/search/")
Игорь Хлебников
źródło
4

Oto, co działa dla mnie:

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

Dir.entrieszwraca tablicę ciągów. Następnie musimy podać pełną ścieżkę do pliku File.file?, chyba że dirjest równa naszemu bieżącemu katalogowi roboczemu. Dlatego to File.join().

yegor256
źródło
1
Musisz wykluczyć „.” i „..” z wpisów
Edgar Ortega
3

Możesz także użyć Rake::FileList(pod warunkiem, że masz rakezależność):

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

Zgodnie z API:

Listy plików są leniwe. Gdy podano listę wzorców globów dla możliwych plików, które mają zostać uwzględnione na liście plików, zamiast przeszukiwać struktury plików w celu znalezienia plików, lista plików zachowuje wzorzec do późniejszego wykorzystania.

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

Artur Beljajev
źródło
1

Jeśli chcesz uzyskać tablicę nazw plików, w tym dowiązania symboliczne , użyj

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

lub nawet

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

a jeśli chcesz przejść bez dowiązań symbolicznych , użyj

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

Jak pokazano w innych odpowiedziach, użyj Dir.glob('/path/to/dir/**/*')zamiast, Dir.new('/path/to/dir')jeśli chcesz uzyskać wszystkie pliki rekurencyjnie.

Michaił Vasin
źródło
Lub po prostu użyj*.*
Richard Peck
1
Dir.new('/home/user/foldername').each { |file| puts file }
Ashwin
źródło
1

Oprócz sugestii w tym wątku chciałem wspomnieć, że jeśli chcesz również zwrócić pliki kropek (.gitignore itp.), W Dir.glob musisz dołączyć flagę: Dir.glob("/path/to/dir/*", File::FNM_DOTMATCH) Domyślnie Dir.entries zawiera pliki kropek, a także bieżące katalogi nadrzędne.

Dla każdego zainteresowanego byłem ciekawy, jak tutaj odpowiedzi w porównaniu do siebie w czasie wykonania, oto wyniki przeciwko głęboko zagnieżdżonej hierarchii. Pierwsze trzy wyniki są nierekurencyjne:

       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)

Zostały one wygenerowane za pomocą następującego skryptu testowego:

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

Różnice w liczbie Dir.entriesplików wynikają z domyślnego uwzględnienia plików ukrytych. Dir.entriesskończyło się to trochę dłużej w tym przypadku z powodu konieczności przebudowania bezwzględnej ścieżki do pliku, aby ustalić, czy plik jest katalogiem, ale nawet bez tego wciąż trwało to dłużej niż inne opcje w przypadku rekurencyjnym. Wszystko to przy użyciu Ruby 2.5.1 na OSX.

Ben Pennell
źródło
1

Jednym prostym sposobem może być:

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

files.each do |f|
    puts f
end
Sebastian Capone
źródło
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

zwraca ścieżki względne pliku z katalogu i wszystkich podkatalogów

punksta
źródło
0

W kontekście IRB możesz użyć następujących poleceń, aby pobrać pliki do bieżącego katalogu:

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

Możesz sprawić, by działało to również w innych katalogach:

file_names = `ls ~/Documents`.split("\n")
Balaji Radhakrishnan
źródło
Te rozwiązania działały dla mnie, ponieważ mam starsze rozwiązanie ze starą wersją ruby, która nie obsługuje polecenia Dir.children
Ciprian Dragoe