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.