Iteruj przez każdy plik w jednym katalogu


274

Jak napisać pętlę w języku ruby, aby móc wykonać blok kodu na każdym pliku?

Jestem nowy w ruby ​​i doszedłem do wniosku, że sposobem na zrobienie tego jest wykonanie każdej pętli.
Plik ruby ​​zostanie wykonany z innego katalogu niż katalog, który chcę zapętlić.

Próbowałem Dir.foreachi nie mogłem go uruchomić.


2
Czy możesz sprecyzować, co się stało, gdy próbujesz go uruchomić? Jaki dokładnie kod próbowałeś (lub odpowiedni fragment, jeśli jest długi)? Jakie otrzymałeś komunikaty o błędach? Dir.foreachdziała w celu iteracji zawartości katalogu, więc dzieje się coś jeszcze.
Telemachus

3
Jeśli chcesz tylko plików w swoim katalogu, nie zapomnij przetestować plików podczas iteracji po zawartości katalogu: do_something_with(entry) if File.file?(entry)
glenn jackman

3
Służy 'img/*.{jpg,png,gif,jpeg}'do przechwytywania wielu rozszerzeń.
Benjamin Crouzier,

@ChrisPeters wydaje się niestety mało prawdopodobne, ponieważ OP nie było na stronie od ponad czterech lat.
Joe Kennedy

Odpowiedzi:


429

Jak powiedzieli inni, Dir::foreachjest tutaj dobrą opcją. Należy jednak pamiętać, że Dir::foreachi Dir::entrieszawsze będzie zawierać .i ..(katalogi bieżący i nadrzędny). Na ogół nie będziesz chciał nad nimi pracować, więc możesz użyć Dir::each_childlub Dir::children(jak sugeruje ma11hew28 ) lub zrobić coś takiego:

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

Dir::foreachoraz Dir::entries(a także Dir::each_childi Dir::children) obejmują również ukryte pliki i katalogi. Często tego właśnie chcesz, ale jeśli tak nie jest, musisz zrobić coś, aby je pominąć.

Ewentualnie możesz sprawdzić, Dir::globktóra zapewnia proste dopasowanie symboli wieloznacznych:

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

12
Użyj, Dir.foreachjeśli katalog zawiera ogromną liczbę plików!
Tilo

5
dzięki! Mały mod, aby uczynić go jeszcze lepszym:next if File.directory? item
mr.buttons

@ mr.buttons To nie zawsze robi dobrze. Czasami ludzie chcą pracować zarówno nad katalogami, jak i plikami. Dałem kod do unikania specjalnych list dla .lub ..dlatego, że ludzie prawie zawsze chcą je zignorować.
Telemachus

3
@Tilo: po prostu z zainteresowania, staram się wyjaśnić bardziej szczegółowo dlaczego? :)
mkataja

11
@ mkataja Dir.foreachiteruje zamiast budować (potencjalnie ogromną) tablicę z góry (co Dir.globrobi). Jeśli więc katalog jest naprawdę ogromny, może mieć to wpływ na wydajność. W normalnych okolicznościach nie zauważysz, ale w warunkach stresowych może to mieć absolutnie znaczenie.
Telemachus,

99

To moja ulubiona metoda łatwego czytania:

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

Możesz nawet rozszerzyć to, aby działało na wszystkich plikach w podkatalogach:

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

30

Dir ma również krótszą składnię, aby uzyskać tablicę wszystkich plików z katalogu:

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

Co w tym kodzie uniemożliwia korzystanie z katalogów również w fnameiteracjach?
kayleeFrye_onDeck

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

2
Alternatywnie użyj Dir # [] lub Dir # glob
Ryan Bigg

13

Biblioteka wyszukiwania została zaprojektowana specjalnie do tego zadania: https://ruby-doc.org/stdlib-2.5.1/libdoc/find/rdoc/Find.html

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

Jest to standardowa biblioteka ruby, więc powinna być dostępna


1
File.findschodzi rekurencyjnie tak daleko, jak to możliwe, zaczynając od ścieżki, którą mu podasz. Nie jestem pewien, czy tego chce OP.
Telemachus

Wydaje mi się, że nie mam dostępu do tej metody - Find.find? Czy muszę pobrać bibliotekę zawierającą tę funkcję?
błękitne niebo

@ user470184: „Znajdź” jest standardową biblioteką ruby ​​i powinna być dostępna z domyślną instalacją ruby. Musisz jednak „znaleźć”, aby móc z niego skorzystać.
Faisal

1
@Faisal mogę przekazać wzorce glob jak *.rbdofind()
Ashhar Hasan

7

Podoba mi się ten, o którym nie wspomniano powyżej.

require 'pathname'

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

Zaletą jest to, że zamiast łańcucha otrzymujesz obiekt Pathname, z którym możesz robić użyteczne rzeczy i przechodzić dalej.


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

1
Oprócz Dir.new ('/ my / dir') istnieją również Dir.entries ('/ my / dir'), ale Dir.foreach () jest nieco bardziej zwięzły.
Tin Man

5
@ZED Również Dir.foreachiteruje, jednocześnie Dir.entriesbudując całą tablicę. Więc jeśli katalog jest ogromny, oznacza to mniejszy hit pamięci. (Prawdopodobnie nie jest to wielka sprawa, ale nadal ...)
Telemachus

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.