2009-08-17 42 views
8

Sto provando a scrivere un po 'di ruby ​​che ricercherebbe in modo ricorsivo una determinata directory per tutte le directory figlio vuote e le rimuoverò.Ruby: come trovo e rimuiamo ricorsivamente le directory vuote?

Pensieri?

Nota: mi piacerebbe una versione di script, se possibile. Questo è sia un bisogno pratico che qualcosa per aiutarmi ad imparare.

+1

primo pensiero: sistema "trova. -type d | xargs rmdir -p 2>/dev/null" – kch

+0

Solo una nota, non voglio fare questa operazione in un colpo dalla riga di comando. Sarà scritto in un rubino. Quello che hai sopra è una versione della linea cmd no? –

+0

beh, è ​​un comando di shell, sì, ma invocato dall'interno di ruby ​​usando 'Kernel.system';) – kch

risposta

15

In ruby:

Dir['**/*']           \ 
    .select { |d| File.directory? d }     \ 
    .select { |d| (Dir.entries(d) - %w[ . .. ]).empty? } \ 
    .each { |d| Dir.rmdir d } 
+0

potresti spiegare "-" nella riga 3? –

+0

Sto sottraendo la matrice contenente le stringhe "." e ".." dall'array delle voci della directory, poiché ogni directory contiene quelle due voci speciali. – kch

+0

inoltre, potresti spiegare a cosa servono i "\"? continuazione della linea? sono necessari? –

2

Perché non usare la shell?

trovare. -type d -empty -exec rmdir '{}' \;

Fa esattamente quello che vuoi.

+0

Speravo in una versione di script ruby ​​per aiutare a comprendere il lavoro con i file. Potrei invocare quanto sopra con 'sh ' da uno script rake suppongo. Hai una versione di script? –

+0

Il comando di script sarebbe esattamente lo stesso. Tuttavia, il post qui sotto ha una risposta che ti si adatta. – koenigdmj

0
Dir.glob('**/*').each do |dir| 
    begin 
    Dir.rmdir dir if File.directory?(dir) 
    # rescue # this can be dangereous unless used cautiously 
    rescue Errno::ENOTEMPTY 
    end 
end 
+0

Salvare da qualsiasi eccezione, volendo o no, non è la migliore idea nel codice di produzione reale. – kch

+0

Di solito, dovrei essere d'accordo, ma questa è una sorta di situazione limite. In ogni caso, ho aggiornato la dichiarazione di salvataggio. – xyz

+0

Sì, è una di queste cose che va bene finché sai cosa stai facendo. Ma un googler che ha colpito questa pagina potrebbe guardarlo solo per uno script veloce o per una libreria che avrà un ruolo importante in un'applicazione intensiva del file system. Quindi, in pratica, n00bs ottiene il codice sicuro per impostazione predefinita e le persone che presumibilmente sanno cosa stanno facendo lo fanno a proprio rischio. – kch

0

Ho testato questo script su OS X, ma se siete su Windows, avrete bisogno di apportare modifiche.

È possibile trovare i file in una directory, inclusi i file nascosti, con voci Dir #.

Questo codice eliminerà le directory che diventano vuote una volta eliminate tutte le sottodirectory.

def entries(dir) 
    Dir.entries(dir) - [".", ".."] 
end 

def recursively_delete_empty(dir) 
    subdirs = entries(dir).map { |f| File.join(dir, f) }.select { |f| File.directory? f } 
    subdirs.each do |subdir| 
    recursively_delete_empty subdir 
    end 

    if entries(dir).empty? 
    puts "deleting #{dir}" 
    Dir.rmdir dir 
    end 
end 
1
module MyExtensions 
    module FileUtils 
    # Gracefully delete dirs that are empty (or contain empty children). 
    def rmdir_empty(*dirs) 
     dirs.each do |dir| 
     begin 
      ndel = Dir.glob("#{dir}/**/", File::FNM_DOTMATCH).count do |d| 
      begin; Dir.rmdir d; rescue SystemCallError; end 
      end 
     end while ndel > 0 
     end 
    end 
    end 

    module ::FileUtils 
    extend FileUtils 
    end 
end 
2
Dir['/Users/path/Movies/incompleteAnime/foom/**/*']. \ 
select { |d| File.directory? d }. \ 
sort.reverse. \ 
each {|d| Dir.rmdir(d) if Dir.entries(d).size == 2} 

proprio come il primo esempio, ma il primo esempio non sembra gestire il bit ricorsivo. L'ordinamento e l'inversione assicurano prima di tutto la gestione delle directory più nidificate.

suppongo sort.reverse potrebbe essere scritto come sort {|a,b| b <=> a} per l'efficienza

3

Bisogna eliminare in ordine inverso, altrimenti se si dispone di un foo directory vuota con una barra sottodirectory si elimina bar, ma non FOO.

Dir.glob(dir + "/**/*").select { |d| 
    File.directory?(d) 
    }.reverse_each { |d| 
    if ((Dir.entries(d) - %w[ . .. ]).empty?) 
     Dir.rmdir(d) 
    end 
    } 
5

Guardando gli esempi da kch, dB. e Vishnu sopra, ho messo insieme una battuta che penso sia una soluzione più elegante:

Dir['**/'].reverse_each { |d| Dir.rmdir d if Dir.entries(d).size == 2 } 

io uso '**/' invece di '/**/*' per il glob, che restituisce solo directory, quindi non ho per verificare se è una directory più tardi. Sto usando reverse_each invece di sort.reverse.each in quanto è più breve e presumibilmente più efficiente, secondo questo post. Io preferisco Dir.entries(d).size == 2 a (Dir.entries(d) - %w[ . .. ]).empty? perché è un po 'più facile da leggere e capire, anche se lo (Dir.entries(d) - %w[ . .. ]).empty? probabilmente funzionerebbe meglio se dovessi eseguire il tuo script su Windows.

Ho provato un po 'su Mac OS X e funziona bene, anche con le directory vuote ricorsive.

+0

Una compilation molto elegante di soluzioni. – Shadow

+1

Un buon refactor ma penso di averlo menzionato. e .. è più intuitivo di size == 2 quindi andrei con 'Dir ['lib/** /']. reverse_each {| d | Dir.rmdir d if Dir.entries (d) .sort ==% w (. ..)} ' – mahemoff

+0

Sì, questo è più intuitivo. – Adam

Problemi correlati