Получить имена всех файлов из папки с Ruby


Ответы:


538

У вас также есть опция ярлыка

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

и если вы хотите найти все файлы Ruby в любой папке или подпапке:

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

5
Или вы можете сделать то же самое с Dir :: glob ()
Йоанн Ле Туш

2
Кроме того, используйте, ./...а не~/
Минь Триет

5
Почему это предпочтительнее?
BvuRVKyUVlViVIc7

1
@MinhTriet что это делает? Что это предпочтительнее?
stephenmurdoch

9
@marflar - ./означает текущий каталог, тогда как /является корневой точкой монтирования и ~/домашним каталогом пользователя. Если вы переместите весь проект куда-то еще, первый будет работать, но два других, вероятно, не будут.
Миричан

170
Dir.entries(folder)

пример:

Dir.entries(".")

Источник: http://ruby-doc.org/core/classes/Dir.html#method-c-entries


15
Похоже, он использует SO для документирования ответов на вопросы, которые он только что задал. Полагаю, что-то вроде записки. Не вижу в этом ничего плохого - в конце концов, хотя этот вопрос немного неполон (например, его Dir#globможно было бы упомянуть), ничто не мешает кому-то еще опубликовать действительно хороший ответ. Конечно, я в основном "наполовину полный стакан", парень ...
Майк Вудхаус

1
@Mike: В общем, это, вероятно, не имеет большого значения. И, как вы говорите, если вопросы и ответы были хорошими, это может быть плюсом в целом для сайта. Но здесь и вопрос, и ответ настолько минимальны, что это не кажется особенно полезным.
Телемах

17
@Telemachus я использую Dirредко, и каждый раз, когда мне это нужно, мне приходится читать документацию. Я разместил здесь свой вопрос и ответ, чтобы найти его позже и, возможно, даже помочь кому-нибудь с тем же вопросом. Я думаю, что я слышал в SO подкасте, что нет ничего плохого в таком поведении. Если у вас есть лучший ответ, пожалуйста, напишите его. Я опубликовал то, что я знаю, я не Рубин ниндзя. Я регулярно принимаю ответы с наибольшим количеством голосов.
Желько Филипин

Это может быть лучшим вариантом , чем Dir[]или , Dir.globесли аргумент является переменной. Когда path = '/tmp', сравните: Dir.glob("#{path}/*")против Dir.entries(path). Возвращаемые значения немного отличаются («.», «..»), но последние легче понять с первого взгляда.
Бенджамин Оукс

92

Следующие фрагменты точно показывает имена файлов внутри каталога, пропустив подкаталоги и ".", ".."пунктирными папки:

Dir.entries("your/folder").select {|f| !File.directory? f}

19
Может также сделать ...select {|f| File.file? f}для более ясного значения и более короткого синтаксиса.
Автомат

2
@squixy Вы правильно написали ?:Dir.entries("your/folder").select {|f| File.file? f}
Automatico

9
Ага. !File.directory?работает, но File.file?нет.
Камил Лелонек

2
@squixy У меня была такая же проблема, в моем случае мне нужно указать полный путь, а не только имя файла, возвращаемое Dir.foreach
TheLukeMcCarthy

6
.reject {|f| File.directory? f}кажется чище чем .select{|f| !File.directory? f}. Ох, и теперь я вижу первый комментарий ... тоже хорошо.
Ян

36

Чтобы получить все файлы (только файлы) рекурсивно:

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

Или все, что не является каталогом ( File.file?будет отклонять нестандартные файлы):

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

Альтернативное решение

Использование Find#findболее подходящего метода поиска, основанного на шаблонах, на Dir.globсамом деле лучше. См. Этот ответ на вопрос: «Однострочное обращение к каталогам рекурсивного списка в Ruby?» ,


18

Это работает для меня:

Если вам не нужны скрытые файлы [1], используйте 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 }

Теперь Dir.entries вернет скрытые файлы, и вам не понадобится подстановочный знак звездочки (вы можете просто передать переменную с именем каталога), но он вернет базовое имя напрямую, поэтому функции File.xxx не будут работать ,

# 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] .dotfileна Unix, я не знаю, о Windows



9

Лично я нашел это наиболее полезным для зацикливания файлов в папке, ориентированной на будущее:

Dir['/etc/path/*'].each do |file_name|
  next if File.directory? file_name 
end

9

Это решение для поиска файлов в каталоге:

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

6

Получая все имена файлов в каталоге, этот фрагмент можно использовать для отклонения как каталогов [ ., ..], так и скрытых файлов, начинающихся с.

files = Dir.entries("your/folder").reject {|f| File.directory?(f) || f[0].include?('.')}

Dir.entriesвозвращает локальные имена файлов, а не абсолютные пути к файлам. С другой стороны, File.directory?ожидает абсолютный путь к файлу. Этот код не работает должным образом.
Натан

Это странно, код не работает в вашем случае. Поскольку это код, который я использовал в живом приложении, которое прекрасно работает. Я перепроверю свой код и опубликую здесь, если в моем исходном рабочем коде чего-то не хватает :)
Lahiru

1
@ Натан Смотрите мой ответ для объяснения


4

Это то, что работает для меня:

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

Dir.entriesвозвращает массив строк. Затем мы должны указать полный путь к файлу File.file?, если он dirне равен нашему текущему рабочему каталогу. Вот почему это File.join().


1
Вы должны исключить "." и ".." из записей
Эдгар Ортега

3

Вы также можете использовать Rake::FileList(если у вас есть rakeзависимость):

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

Согласно API:

Списки файлов ленивы. Когда предоставляется список шаблонов glob для возможных файлов, которые будут включены в список файлов, вместо поиска файловых структур для поиска файлов, FileList содержит шаблон для последующего использования.

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


1

Если вы хотите получить массив имен файлов, включая символические ссылки , используйте

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

или даже

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

и если вы хотите обойтись без символических ссылок , используйте

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

Как показано в других ответах, используйте Dir.glob('/path/to/dir/**/*')вместо, Dir.new('/path/to/dir')если вы хотите получить все файлы рекурсивно.


Или просто использовать*.*
Ричард Пек


1

В дополнение к предложениям в этой теме я хотел бы упомянуть, что если вам нужно также возвращать точечные файлы (.gitignore и т. Д.), То в Dir.glob вам нужно будет включить флаг следующим образом: Dir.glob("/path/to/dir/*", File::FNM_DOTMATCH) по умолчанию Dir.entries включает в себя точечные файлы, а также текущие родительские каталоги.

Для всех, кому было интересно, мне было любопытно, как ответы здесь сравниваются друг с другом во время выполнения, вот результаты против глубоко вложенной иерархии. Первые три результата не являются рекурсивными:

       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)

Они были сгенерированы с помощью следующего сценария тестирования:

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

Различия в количестве файлов обусловлены Dir.entriesвключением скрытых файлов по умолчанию. Dir.entriesв этом случае потребовалось немного больше времени из-за необходимости перестроить абсолютный путь к файлу, чтобы определить, является ли файл каталогом, но даже без этого он все еще занимал значительно больше времени, чем другие параметры в рекурсивном случае. Это все с использованием ruby ​​2.5.1 на OSX.


1

Один простой способ может быть:

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

files.each do |f|
    puts f
end

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

возвращает относительные пути файла из каталога и всех подкаталогов


0

В контексте IRB вы можете использовать следующее для получения файлов в текущем каталоге:

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

Вы можете сделать эту работу и в других каталогах:

file_names = `ls ~/Documents`.split("\n")

Это решение сработало для меня, так как у меня есть устаревшее решение со старой версией ruby, которая не поддерживает команду Dir.children
Ciprian Dragoe
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.