2011-11-02 12 views
9

Sono nuovo di Ruby (essendo uno sviluppatore Java) e sto cercando di implementare un metodo (oh, mi dispiace, una funzione) che recupera e restituisce tutti i file nelle sottodirectory in modo ricorsivo.elenco di file ricorsivo in Ruby

Ho implementato come:

def file_list_recurse(dir) 
    Dir.foreach(dir) do |f| 
    next if f == '.' or f == '..' 
    f = dir + '/' + f 
    if File.directory? f 
     file_list_recurse(File.absolute_path f) { |x| yield x } 
    else 
     file = File.new(f) 
     yield file 
    end 
    end 
end 

Le mie domande sono:

  1. fa File.new davvero aprire un file? In Java nuovo File ("xxx") non ... Se ho bisogno di fornire qualche struttura che potrei interrogare informazioni sui file (ctime, dimensione, ecc) da cosa sarebbe in Ruby?
  2. {| x | yield x} mi sembra un po 'strano, è OK fare rendimenti da funzioni ricorsive del genere, o c'è un modo per evitarlo?
  3. C'è un modo per evitare il controllo di '.' e '..' su ogni iterazione?
  4. C'è un modo migliore per implementare questo?

Grazie

PS: l'uso del campione del mio metodo è qualcosa di simile:

curr_file = nil 

file_list_recurse('.') do |file| 
    curr_file = file if curr_file == nil or curr_file.ctime > file.ctime 
end 

puts curr_file.to_path + ' ' + curr_file.ctime.to_s 

(che sarebbe ottenere il file più vecchio dall'albero)

= =========

Quindi, grazie a @buruza Emon ho scoperto la grande funzione di Dir.glob che mi ha salvato un paio di righe di codice. Inoltre, grazie a @Casper ho scoperto il metodo File.stat, che ha reso la mia funzione corsa due volte più veloce che con File.new

Alla fine il mio codice sta cercando qualcosa di simile:

i=0 
curr_file = nil 

Dir.glob('**/*', File::FNM_DOTMATCH) do |f| 
    file = File.stat(f) 
    next unless file.file? 
    i += 1 
    curr_file = [f, file] if curr_file == nil or curr_file[1].ctime > file.ctime 
end 

puts curr_file[0] + ' ' + curr_file[1].ctime.to_s 
puts "total files #{i}" 

=====

per impostazione predefinita Dir.glob ignora i nomi di file che iniziano con un punto (considerato da 'nascosto' in * nix), quindi è molto importante aggiungere il secondo file argomento :: FNM_DOTMATCH

risposta

5

questa cosa mi dice di considerare di accettare una risposta, spero che non mi dispiacerebbe rispondermi da solo:

i=0 
curr_file = nil 

Dir.glob('**/*', File::FNM_DOTMATCH) do |f| 
    file = File.stat(f) 
    next unless file.file? 
    i += 1 
    curr_file = [f, file] if curr_file == nil or curr_file[1].ctime > file.ctime 
end 

puts curr_file[0] + ' ' + curr_file[1].ctime.to_s 
puts "total files #{i}" 
2

È possibile utilizzare lointegratomodulo find metodo.

11

Che ne dici di questo?

puts Dir['**/*.*'] 
+0

che è grande!Ma produce un array di oggetti String. Quello che sto cercando è una funzione che produrrebbe una struttura simile a File in modo che potessi fare i miei calcoli basati su quello. Trovare il file più grande, il primo ctime ecc. –

+0

Dir ['.'] Non accetta un blocco. Ma Dir.glob lo fa! Risponde alle mie domande, ad eccezione della domanda n. –

5

Secondo i documenti File.new apre il file. Potresti invece utilizzare File.stat, che raccoglie le statistiche relative ai file in un oggetto interrogabile. Ma nota che le statistiche sono raccolte al momento della creazione. Non quando si chiamano i metodi di query come ctime.

Esempio:

Dir['**/*'].select { |f| File.file?(f) }.map { |f| File.stat(f) } 
+1

File.stat ironicamente non fornisce il nome del file, quindi non posso usarlo come oggetto dati per tornare dal mio metodo. Inoltre, ho un albero di 200.000 file. L'esecuzione del tuo esempio porta a un processo di ruby ​​che supera i 60 Mb, mentre il mio metodo (anche con File.new) non fa mai andare il rubino sopra i 6 Mb. (Sto testando con watch -n 0,1 "ps ax -o comm, rss | grep ruby ​​>>/tmp/q"). Ma la linea di codice di esempio sembra davvero interessante ;-) –

Problemi correlati