รับชื่อไฟล์ทั้งหมดจากโฟลเดอร์ด้วย Ruby


คำตอบ:


537

คุณยังมีตัวเลือกทางลัดของ

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

และหากคุณต้องการค้นหาไฟล์ทับทิมทั้งหมดในโฟลเดอร์หรือโฟลเดอร์ย่อย:

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

5
หรือคุณสามารถทำเช่นเดียวกันกับ Dir :: glob ()
Yoann Le Touche

2
ใช้./...มากกว่า~/
Minh Triet

5
ทำไมถึงชอบ
BvuRVKyUVlViVIc7

1
@MinTriet ทำอะไรได้บ้าง มันคืออะไรดีกว่า
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
@ ไมค์: ในรูปแบบที่ยิ่งใหญ่ของสิ่งต่าง ๆ มันอาจไม่ใช่เรื่องใหญ่ และอย่างที่คุณพูดหากคำถามและคำตอบนั้นดีอาจเป็นประโยชน์โดยรวมสำหรับไซต์ แต่ที่นี่ทั้งคำถามและคำตอบมีน้อยมากจนไม่เป็นประโยชน์โดยเฉพาะ
Telemachus

17
@ Telemachus ฉันใช้Dirน้อยมากและทุกครั้งที่ฉันต้องการฉันก็ต้องอ่านเอกสาร ฉันโพสต์คำถามและคำตอบที่นี่เพื่อหาได้ในภายหลังและอาจช่วยคนที่มีคำถามเดียวกัน ฉันคิดว่าฉันเคยได้ยินที่พอดคาสต์ดังนั้นไม่มีอะไรผิดปกติกับพฤติกรรมดังกล่าว หากคุณมีคำตอบที่ดีกว่าโปรดโพสต์มัน ฉันโพสต์สิ่งที่ฉันรู้แล้วฉันไม่ใช่นินจาทับทิม ฉันยอมรับคำตอบอย่างสม่ำเสมอด้วยคะแนนโหวตมากที่สุด
Noveljko Filipin

นี่อาจเป็นตัวเลือกที่ดีกว่าDir[]หรือDir.globเมื่ออาร์กิวเมนต์เป็นตัวแปร เมื่อpath = '/tmp'เปรียบเทียบ: VSDir.glob("#{path}/*") Dir.entries(path)ค่าส่งคืนจะแตกต่างกันเล็กน้อย (".", ".. "), แต่อย่างหลังนั้นง่ายกว่าที่จะติดตามอย่างรวดเร็ว
Benjamin Oakes

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?ไม่
Kamil Lelonek

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จะส่งคืนไฟล์ที่ซ่อนอยู่และคุณไม่จำเป็นต้องใช้เครื่องหมายดอกจัน asterix (คุณสามารถส่งตัวแปรที่มีชื่อไดเรกทอรี) แต่มันจะคืนค่าชื่อฐานโดยตรงดังนั้นฟังก์ชัน 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บนยูนิกซ์ฉันไม่รู้เกี่ยวกับ 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
คุณต้องยกเว้น "." และ ".. " จากรายการ
Edgar Ortega

3

คุณอาจต้องการใช้Rake::FileList(หากคุณต้องrakeพึ่งพา):

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

ตาม API:

FileLists ขี้เกียจ เมื่อได้รับรายการรูปแบบ glob สำหรับไฟล์ที่เป็นไปได้ที่จะรวมอยู่ในรายการไฟล์แทนที่จะค้นหาโครงสร้างไฟล์เพื่อค้นหาไฟล์ FileList จะเก็บรูปแบบไว้เพื่อใช้ในภายหลัง

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


1

หากคุณต้องการอาร์เรย์ของชื่อไฟล์รวมถึง symlinkให้ใช้

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')หากคุณต้องการรับไฟล์ทั้งหมดซ้ำ


หรือเพียงใช้*.*
Richard Peck


1

นอกจากคำแนะนำในหัวข้อนี้ฉันต้องการพูดถึงว่าหากคุณต้องการส่งคืนไฟล์ dot เช่นกัน (.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



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")

การแก้ปัญหานี้ทำงานให้ฉันตั้งแต่ฉันมีวิธีการแก้ปัญหาแบบเดิมกับรุ่นทับทิมเก่าที่ไม่สนับสนุนคำสั่ง Dir.children
ปริ Dragoe
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.