Ітерація через кожен файл в одному каталозі


274

Як записати цикл в рубін, щоб я міг виконати блок коду на кожному файлі?

Я новачок у рубіні, і я зробив висновок, що спосіб це зробити - це робити кожен цикл.
Файл рубіну буде виконаний з іншого каталогу, ніж каталог, через який я хочу пройти цикл.

Я спробував, Dir.foreachі я не міг змусити його працювати.


2
Чи можете ви вказати, що сталося, коли ви намагалися змусити його працювати? Який точний код ви спробували (або відповідний фрагмент, якщо він довгий)? Які повідомлення про помилки ви отримали? Dir.foreachпрацює над ітерацією вмісту каталогу, тому щось ще багато чого відбувається.
Телемах

3
Якщо ви хочете лише файли у вашому каталозі, не забудьте перевірити наявність файлів під час повторення вмісту каталогу: do_something_with(entry) if File.file?(entry)
glenn jackman

3
Використовуйте 'img/*.{jpg,png,gif,jpeg}'для захоплення кількох розширень.
Бенджамін Крозьє

@ChrisPeters здається малоймовірним, на жаль, оскільки ОП не було на місці вже більше чотирьох років.
Джо Кеннеді

Відповіді:


429

Як говорили інші, Dir::foreachтут хороший варіант. Тим НЕ менше, зверніть увагу , що Dir::foreachі Dir::entriesзавжди буде включати в себе .і ..(поточні і батьківські каталоги). Ви, як правило, не захочете працювати над ними, тому можете використовувати Dir::each_childабо Dir::children(як це запропонував ma11hew28 ) або робити щось подібне:

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

Dir::foreachі Dir::entries(а також Dir::each_childі Dir::children) також включають в себе приховані файли & каталоги. Часто це саме те, що ти хочеш, але якщо це не так, потрібно щось зробити, щоб пропустити їх.

Крім того, ви можете розглянути, Dir::globщо забезпечує просту відповідність підстановки:

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

12
Використовуйте, Dir.foreachякщо каталог містить величезну кількість файлів!
Тіло

5
Дякую! Невеликий мод, щоб зробити це ще краще:next if File.directory? item
mr.buttons

@ mr.buttons Це не завжди зробить правильно. Іноді люди хочуть працювати над каталогами , а також файлами. Я дав код для уникнення спеціальних списків для .або ..тому, що люди майже завжди хочуть ігнорувати ці два.
Телемах

3
@Tilo: просто з інтересу, будь ласка, поясніть трохи детальніше, чому? :)
mkataja

11
@mkataja ітератує, Dir.foreachа не створює (потенційно величезний) масив вперед (що Dir.globі є). Тож якщо каталог справді величезний, він може змінити продуктивність. У звичайних обставинах ви цього не помітили, але в умовах стресу це може мати значення.
Телемах

99

Це мій улюблений метод, який легко читати:

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

І ви навіть можете розширити це для роботи над усіма файлами у підкаталогах:

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

30

У Dir також є коротший синтаксис, щоб отримати масив усіх файлів з каталогу:

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

Що в цьому коді заважає каталогів також використовуватись з fnameітераціями 's?
kayleeFrye_onDeck


13

Бібліотека пошуку призначена саме для цього завдання: https://ruby-doc.org/stdlib-2.5.1/libdoc/find/rdoc/Find.html

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

Це стандартна бібліотека рубінів, тому вона повинна бути доступною


1
File.findрекурсивно йде вниз, наскільки це можливо, починаючи з будь-якого шляху, який ви йому надаєте. Я не впевнений, що цього хоче ОП.
Телемах

Я не маю доступу до цього методу - Find.find? Чи потрібно завантажувати бібліотеку, включаючи цю функціональність?
блакитно-небо

@ user470184: "Знайти" - це стандартна бібліотека рубінів, і вона повинна бути доступною при встановленні рубіну за замовчуванням. Однак вам потрібно "вимагати" знайти ", перш ніж ви зможете ним скористатися.
Faisal

1
@Faisal Можу чи я передати зразки Глоб як *.rbдоfind()
Ashhar Hasan

7

Мені подобається цей, про який не було сказано вище.

require 'pathname'

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

Перевага полягає в тому, що ви отримуєте об'єкт Pathname замість рядка, з яким ви можете робити корисні речі та рухатись далі.


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

1
Окрім Dir.new ('/ my / dir') є також Dir.entries ('/ my / dir'), але Dir.foreach () є трохи більш лаконічним.
Олов'яний чоловік

5
@ ZED Також Dir.foreachповторює процес одночасного Dir.entriesформування всього масиву. Отже, якщо каталог величезний, це менше потрапляння пам'яті. (Мабуть, не велика справа, напевно, але все ж ...)
Телемах

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.