Перебирать каждый файл в одном каталоге

274

Как мне написать цикл в ruby, чтобы я мог выполнить блок кода для каждого файла?

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

Я попробовал, Dir.foreachи я не мог заставить его работать.

Андрей
источник
2
Можете ли вы указать, что произошло, когда вы пытались заставить его работать? Какой точный код вы пробовали (или соответствующий кусок, если он длинный)? Какие сообщения об ошибках вы получили? Dir.foreachработает, чтобы перебрать содержимое каталога, так что еще много чего происходит.
Телемах
3
Если вы хотите, чтобы файлы do_something_with(entry) if File.file?(entry)
находились
3
Используйте, 'img/*.{jpg,png,gif,jpeg}'чтобы получить несколько расширений.
Бенджамин Крузье
@ChrisPeters, к сожалению, кажется маловероятным, поскольку ОП не был на сайте более четырех лет.
Джо Кеннеди

Ответы:

430

Как уже говорили другие, 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
@ Тило: просто из интереса, попробуйте объяснить более подробно, почему? :)
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
SimplGy
источник
30

Dir также имеет более короткий синтаксис для получения массива всех файлов из каталога:

Dir['dir/to/files/*'].each do |fname|
    # do something with fname
end
wawka
источник
Что в этом коде предотвращает использование каталогов fnameс итерациями?
kayleeFrye_onDeck
26
Dir.foreach("/home/mydir") do |fname|
  puts fname
end
Фред
источник
2
В качестве альтернативы используйте Dir # [] или Dir # glob
Райан Бигг
13

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

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

Это стандартная библиотека ruby, поэтому она должна быть доступна

Фейсал
источник
1
File.findидет рекурсивно вниз, насколько это возможно, начиная с любого пути, который вы ему дадите. Я не уверен, что именно этого хочет ОП.
Телемах
Кажется, у меня нет доступа к этому методу - Find.find? Нужно ли загружать библиотеку, чтобы не включать эту функцию?
голубое небо
@ user470184: «Найти» - это стандартная библиотека ruby, которая должна быть доступна при установке ruby ​​по умолчанию. Однако вам необходимо «потребовать« найти », прежде чем вы сможете его использовать.
Фейсал
1
@Faisal Могу ли я передать образцы Глоб как *.rbкfind()
Ashhar Hasan
7

Мне нравится этот, который не был упомянут выше.

require 'pathname'

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

Преимущество состоит в том, что вместо строки вы получаете объект Pathname, с которым вы можете делать полезные вещи и проходить дальше.

skagedal
источник
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строит весь массив одновременно. Так что, если каталог огромен, это меньше попадания в память. (Не обычно большое дело, вероятно, но все же ...)
Telemachus
2

Чтобы пропустить .& .., вы можете использовать Dir::each_child.

Dir.each_child('/path/to/dir') do |filename|
  puts filename
end

Dir::children возвращает массив имен файлов.

ma11hew28
источник