Dapatkan nama semua file dari folder dengan Ruby

358

Saya ingin mendapatkan semua nama file dari folder menggunakan Ruby.

Željko Filipin
sumber

Jawaban:

538

Anda juga memiliki opsi pintasan

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

dan jika Anda ingin menemukan semua file Ruby di folder atau sub-folder apa pun:

Dir["/path/to/search/**/*.rb"]
Ian Eccles
sumber
5
Atau Anda dapat melakukan hal yang sama dengan Dir :: glob ()
Yoann Le Touche
2
Juga, gunakan ./...daripada~/
Minh Triet
5
Mengapa ini lebih disukai?
BvuRVKyUVlViVIc7
1
@MinhTriet apa fungsinya? Apa yang lebih disukai?
stephenmurdoch
9
@marflar - ./berarti direktori saat ini, sedangkan /adalah titik mount root, dan ~/merupakan direktori home pengguna. Jika Anda memindahkan seluruh proyek ke tempat lain, yang pertama akan berhasil, tetapi dua lainnya mungkin tidak.
mirichan
170
Dir.entries(folder)

contoh:

Dir.entries(".")

Sumber: http://ruby-doc.org/core/classes/Dir.html#method-c-entries

Željko Filipin
sumber
15
Sepertinya dia menggunakan SO untuk mendokumentasikan jawaban atas pertanyaan yang baru saja dia tanyakan. Semacam memo, kurasa. Tidak dapat melihat banyak yang salah dengan itu - setelah semua, meskipun yang ini sedikit tidak lengkap ( Dir#globmungkin bisa disebutkan, misalnya) tidak ada yang mencegah orang lain dari memposting Jawaban yang Benar-Benar Baik. 'Tentu saja, saya sebagian besar jenis pria "gelas setengah penuh" ...
Mike Woodhouse
1
@ Mike: Dalam skema besar, mungkin bukan masalah besar. Dan seperti yang Anda katakan jika pertanyaan dan jawabannya bagus, itu bisa menjadi nilai tambah keseluruhan untuk situs tersebut. Tetapi di sini baik pertanyaan maupun jawaban sangat minim sehingga tampaknya tidak terlalu berguna.
Telemachus
17
@ Telemakus saya Dirjarang menggunakan , dan setiap kali saya membutuhkannya saya harus membaca dokumentasi. Saya telah memposting pertanyaan dan jawaban saya di sini sehingga saya dapat menemukannya nanti, dan mungkin bahkan membantu seseorang dengan pertanyaan yang sama. Saya pikir saya telah mendengar di SO podcast bahwa tidak ada yang salah dengan perilaku seperti itu. Jika Anda memiliki jawaban yang lebih baik, silakan kirim. Saya telah memposting apa yang saya ketahui, saya bukan seorang ninja Ruby. Saya secara teratur menerima jawaban dengan suara terbanyak.
Željko Filipin
Ini bisa menjadi pilihan yang lebih baik daripada Dir[]atau Dir.globketika argumen adalah variabel. Ketika path = '/tmp', membandingkan: Dir.glob("#{path}/*")vs Dir.entries(path). Nilai kembali sedikit berbeda (".", ".."), tetapi yang terakhir lebih mudah untuk dilihat sekilas.
Benjamin Oakes
92

Cuplikan berikut persis menunjukkan nama file di dalam direktori, melewatkan subdirektori dan ".", ".."folder bertitik:

Dir.entries("your/folder").select {|f| !File.directory? f}
Emiliano Poggi
sumber
19
Dapat juga dilakukan ...select {|f| File.file? f}untuk makna yang lebih jelas dan sintaks yang lebih pendek.
Otomatis
2
@ skixy Apakah Anda menuliskannya dengan benar ?:Dir.entries("your/folder").select {|f| File.file? f}
Automatico
9
Ya. !File.directory?sedang bekerja tetapi File.file?tidak.
Kamil Lelonek
2
@ squixy Saya memiliki masalah yang sama, dalam kasus saya, saya harus memberikan path lengkap bukan hanya nama file yang dikembalikan oleh Dir.foreach
TheLukeMcCarthy
6
.reject {|f| File.directory? f}tampaknya lebih bersih dari .select{|f| !File.directory? f}. Oh, dan sekarang saya melihat komentar pertama ... juga bagus.
Ian
36

Untuk mendapatkan semua file (hanya file) secara rekursif:

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

Atau apa pun yang bukan direktori ( File.file?akan menolak file tidak biasa):

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

Solusi alternatif

Menggunakan Find#findlebih dari metode pencarian berbasis pola seperti Dir.globsebenarnya lebih baik. Lihat jawaban ini untuk "Satu-liner ke Daftar Direktori secara Rekursif di Ruby?" .

konsolebox
sumber
18

Ini bekerja untuk saya:

Jika Anda tidak ingin file tersembunyi [1], gunakan 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 }

Sekarang, Dir.entries akan mengembalikan file tersembunyi, dan Anda tidak perlu wildcard asterix (Anda bisa meneruskan variabel dengan nama direktori), tetapi itu akan mengembalikan nama basenya langsung, sehingga fungsi File.xxx tidak akan berfungsi .

# 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] .dotfiledi unix, saya tidak tahu tentang Windows


sumber
14

Di Ruby 2.5 sekarang Anda dapat menggunakan Dir.children. Itu mendapatkan nama file sebagai array kecuali untuk "." dan ".."

Contoh:

Dir.children("testdir")   #=> ["config.h", "main.rb"]

http://ruby-doc.org/core-2.5.0/Dir.html#method-c-children

Mario Pérez
sumber
9

Secara pribadi, saya menemukan ini yang paling berguna untuk pengulangan file dalam folder, melihat ke depan keamanan:

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

Ini adalah solusi untuk menemukan file di direktori:

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
gilcierweb
sumber
6

Saat mendapatkan semua nama file dalam direktori, snippet ini dapat digunakan untuk menolak direktori [ ., ..] dan file tersembunyi yang dimulai dengan.

files = Dir.entries("your/folder").reject {|f| File.directory?(f) || f[0].include?('.')}
Lahiru
sumber
Dir.entriesmengembalikan nama file lokal, bukan path file absolut. Di sisi lain, File.directory?mengharapkan jalur file absolut. Kode ini tidak berfungsi seperti yang diharapkan.
Nathan
Sungguh aneh kodenya tidak berfungsi dalam kasus Anda. Karena ini adalah kode yang saya gunakan di aplikasi langsung yang berfungsi dengan baik. Saya akan memeriksa ulang kode saya dan memposting di sini jika ada sesuatu yang hilang dari kode kerja asli saya :)
Lahiru
1
@Nathan Lihat jawaban saya untuk penjelasan
5

kode ini hanya mengembalikan nama file dengan ekstensi mereka (tanpa jalur global)

Dir.children("/path/to/search/")
Игорь Хлебников
sumber
4

Inilah yang bekerja untuk saya:

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

Dir.entriesmengembalikan array string. Kemudian, kita harus menyediakan path lengkap file File.file?, kecuali dirsama dengan direktori kerja kita saat ini. Itu sebabnya ini File.join().

yegor256
sumber
1
Anda harus mengecualikan "." dan ".." dari entri
Edgar Ortega
3

Anda mungkin juga ingin menggunakan Rake::FileList(asalkan Anda memiliki rakeketergantungan):

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

Menurut API:

FileLists malas. Ketika diberi daftar pola glob untuk kemungkinan file yang akan dimasukkan dalam daftar file, alih-alih mencari struktur file untuk menemukan file, FileList memegang pola untuk penggunaan terakhir.

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

Artur Beljajev
sumber
1

Jika Anda ingin mendapatkan array nama file termasuk symlink , gunakan

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

atau bahkan

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

dan jika Anda ingin pergi tanpa symlink , gunakan

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

Seperti yang ditunjukkan dalam jawaban lain, gunakan Dir.glob('/path/to/dir/**/*')alih-alih Dir.new('/path/to/dir')jika Anda ingin mendapatkan semua file secara rekursif.

Mikhail Vasin
sumber
Atau cukup gunakan*.*
Richard Peck
1
Dir.new('/home/user/foldername').each { |file| puts file }
Ashwin
sumber
1

Selain saran di utas ini, saya ingin menyebutkan bahwa jika Anda perlu mengembalikan file dot juga (.gitignore, dll), dengan Dir.glob Anda harus menyertakan bendera sebagai berikut: Dir.glob("/path/to/dir/*", File::FNM_DOTMATCH) Secara default, Dir.entries termasuk file dot, serta direktori induk saat ini.

Bagi siapa pun yang tertarik, saya ingin tahu bagaimana jawaban di sini dibandingkan satu sama lain dalam waktu pelaksanaan, di sini adalah hasil terhadap hierarki yang sangat bersarang. Tiga hasil pertama adalah non-rekursif:

       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)

Ini dihasilkan dengan skrip pembandingan berikut:

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

Perbedaan dalam jumlah file adalah karena Dir.entriesmemasukkan file tersembunyi secara default. Dir.entriesakhirnya mengambil sedikit lebih lama dalam kasus ini karena perlu membangun kembali path absolut file untuk menentukan apakah file adalah direktori, tetapi bahkan tanpa itu masih secara konsisten memakan waktu lebih lama daripada opsi lain dalam kasus rekursif. Ini semua menggunakan ruby ​​2.5.1 di OSX.

Ben Pennell
sumber
1

Salah satu cara sederhana adalah:

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

files.each do |f|
    puts f
end
Sebastian Capone
sumber
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

mengembalikan jalur relatif file dari direktori dan semua subdirektori

punksta
sumber
0

Dalam konteks IRB, Anda dapat menggunakan yang berikut ini untuk mendapatkan file di direktori saat ini:

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

Anda dapat membuat ini berfungsi di direktori lain juga:

file_names = `ls ~/Documents`.split("\n")
Balaji Radhakrishnan
sumber
Solusi ini bekerja untuk saya karena saya memiliki solusi lawas dengan versi ruby ​​lama yang tidak mendukung perintah Dir.children
Ciprian Dragoe