Отримайте імена всіх файлів із папки за допомогою Ruby


Відповіді:


537

У вас також є варіант ярлика

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

і якщо ви хочете знайти всі файли Ruby у будь-якій папці чи підпапці:

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

5
Або ви можете зробити те ж саме з Dir :: glob ()
Йоанн Ле Туш

2
Також скористайтеся, ./...а не~/
Minh Triet

5
Чому це віддається перевазі?
BvuRVKyUVlViVIc7

1
@MinhTriet що це робить? Що переважніше?
Stephenmurdoch

9
@marflar - ./означає поточний каталог, тоді як /точка кореневого монтажу і ~/є домашньою каталогом користувача. Якщо весь проект перенести кудись інше, перший працюватиме, але два інших, мабуть, не будуть.
mirichan

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}можна зрозуміти значення та скоротити синтаксис.
Automatico

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
@Nathan Дивіться мою відповідь для пояснення


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:

Списки файлів ледачі. Коли дано список глобальних шаблонів для можливих файлів, які повинні бути включені до списку файлів, замість пошуку файлових структур для пошуку файлів, 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у цьому випадку в цьому випадку пішло трохи більше часу через необхідність відновлення абсолютного шляху до файлу, щоб визначити, чи файл є каталогом, але навіть без цього він тривав послідовно довше, ніж інші параметри рекурсивного випадку. Для цього все використовували рубін 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.