Come rimuovere tutte le directory vuote in una sottostruttura?


151

Come posso rimuovere tutte le directory vuote in una sottostruttura? Ho usato qualcosa del genere

find . -type d -exec rmdir {} 2>/dev/null \;

ma devo essere eseguito più volte per rimuovere solo le directory contenenti directory vuote. Inoltre, è abbastanza lento, specialmente con Cygwin.


Vedi anche emacs.stackexchange.com/q/12190/2264 per una soluzione emacs.
Sean Allred,

Risposte:


222

Combinando findopzioni e predicati GNU , questo comando dovrebbe fare il lavoro:

find . -type d -empty -delete
  • -type d limita alle directory
  • -empty si limita a svuotare quelli
  • -delete rimuove ogni directory

L'albero è camminato dalle foglie senza la necessità di specificare -depthcome è implicito da -delete.


2
-deleteimplica già -depthquindi non è necessario specificarlo manualmente.
Jamadagni,

1
Grazie, non me ne sono reso conto. Risposta aggiornata
Christophe Drevet-Droguet,

11
Vorrei aggiungere -mindepth 1qui, per evitare di eliminare la directory iniziale stessa, se fosse vuota.
Greg Dubicki,

2
Fantastico, ma non funziona sui miei vecchi host SunOS ...
dokaspar

2
!ha un significato speciale per la shell. Devi scappare. Qualcosa del genere: \! -name 'Completed'poco prima -deletedovrebbe funzionare. Oppure inserisci semplicemente un file marker in questa directory.
Christophe Drevet-Droguet,

53

Elenca prima le directory profondamente annidate.

find . -depth -type d -exec rmdir {} \; 2>/dev/null

(Si noti che il reindirizzamento si applica al findcomando nel suo insieme, non solo a rmdir. Il reindirizzamento solo per rmdircauserebbe un rallentamento significativo poiché sarebbe necessario invocare una shell intermedia.)

È possibile evitare l'esecuzione rmdirsu directory non vuote passando il -emptypredicato da trovare. GNU find verifica la directory quando sta per eseguire il comando, quindi verranno prese le directory che sono state appena svuotate.

find . -depth -type d -empty -exec rmdir {} \;

Un altro modo per accelerare sarebbe raggruppare le rmdirinvocazioni. È probabile che entrambi siano notevolmente più veloci dell'originale, specialmente con Cygwin. Non mi aspetto molta differenza tra questi due.

find . -depth -type d -print0 | xargs -0 rmdir 2>/dev/null
find . -depth -type d -exec rmdir {} + 2>/dev/null

Quale metodo è più veloce dipende da quante directory non vuote hai. Non puoi combinare -emptycon i metodi per raggruppare le invocazioni, perché le directory che contengono solo directory vuote non sono vuote quando le findguardano.

Un altro metodo sarebbe quello di eseguire più passaggi. La maggiore rapidità dipende da molte cose, incluso se l'intera gerarchia di directory può rimanere nella cache del disco tra le findesecuzioni.

while [ -n "$(find . -depth -type d -empty -print -exec rmdir {} +)" ]; do :; done

In alternativa, usa zsh. Il qualificatore glob F corrisponde a directory non vuote, quindi /^Fcorrisponde a directory vuote. Le directory che contengono solo directory vuote non possono essere abbinate così facilmente.

while rmdir **/*(/N^F); do :; done

(Questo termina quando rmdirriceve una riga di comando vuota.)


Questo è tutto. Invece di 90 secondi ci vogliono 0,90 s.
maaartino

@maaartinus: sono curioso: hai un set di dati simile in cui puoi provare senza -p? Non avrei pensato che avrebbe fatto la differenza.
Gilles

3
@maartinus - altre piccole ottimizzazioni: l'aggiunta -emptydovrebbe funzionare con questa (anche se non sono sicuro di quanto guadagnerà). E molto, molto banalmente, dal momento che probabilmente non si desidera rimuovere ., utilizzare -mindepth 1.
Mattdm,

Non è stata la rimozione, ma il processo si è avviato dall'alto, ciò che ha richiesto quasi tutto il tempo. Avevo trascurato l' -depthargomento, il che rende rmdir -pinutile. Ho già cambiato il mio commento. Gli anni '90 erano il mio tentativo originale; non c'è niente di sorprendente qui.
maaartino

2
Mi sono reso conto che possiamo rimuovere del rmdirtutto la chiamata di comando, almeno con GNU find, con questo comando:find . -depth -type d -empty -delete
Christophe Drevet-Droguet,

6

Se hai appena virare una -psul vostro rmdir, che sarà il lavoro in un solo passaggio. Non sarà bello o ottimale, ma dovrebbe ottenere tutto. Ciò dice a rmdir di rimuovere tutte le directory principali non vuote di quella che stai rimuovendo.

Puoi salvare un po 'aggiungendo il -emptytest per trovare, in modo che non si preoccupi delle directory non vuote.


3

find . -depth -type d -exec rmdir {} +

è la risposta più semplice e standard conforme a questa domanda.

Le altre risposte fornite qui purtroppo dipendono tutte da miglioramenti specifici del fornitore che non esistono su tutti i sistemi.


3
Questa risposta genera un errore per ogni directory che non può essere cancellato, che può essere inferiore a desiderabile.
Willem van Ketwich,

0

find . -type d -printf "%d %p\n" |\ sort -nr |\ perl -pe 's/^\d+\s//;' |\ while read dir; do \ (rmdir "$dir" > /dev/null 2>&1); \ done

Ecco come funziona:

  1. Elenca ricorsivamente tutte le directory insieme alla loro profondità
  2. Ordina per ordine decrescente della loro profondità
  3. Filtra solo i percorsi di directory
  4. Esegui rmdirnell'elenco uno per uno

0

Uso questi alias per findcomandi usati di frequente , specialmente quando pulisco lo spazio su disco usando dupeguru , dove la rimozione di duplicati può comportare molte directory vuote.

Commenti all'interno, .bashrcquindi non li dimenticherò più tardi quando dovrò modificarlo.

# find empty directories
alias find-empty='find . -type d -empty'

# fine empty/zero sized files
alias find-zero='find . -type f -empty'

# delete all empty directories!
alias find-empty-delete='find-empty -delete'

# delete empty directories when `-delete` option is not available.
# output null character (instead of newline) as separator. used together
# with `xargs -0`, will handle filenames with spaces and special chars.
alias find-empty-delete2='find-empty -print0 | xargs -0 rmdir -p'

# alternative version using `-exec` with `+`, similar to xargs.
# {}: path of current file
# +: {} is replaced with as many pathnames as possible for each invocation.
alias find-empty-delete3='find-empty -exec rmdir -p {} +'

# for removing zero sized files, we can't de-dupe them automatically
# since they are technically all the same, so they are typically left
# beind. this removes them if needed.
alias  find-zero-delete='find-zero -delete'
alias find-zero-delete2='find-zero -print0 | xargs -0 rm'
alias find-zero-delete3='find-zero -exec rm {} +'

-2

rm -r */il comando ha funzionato facilmente per me. rmdovrebbe richiedere la -frimozione forzata di directory con file. rm -rdovrebbe rimuovere solo le directory vuote. Sono aperto al perché questo potrebbe essere sbagliato. Questo dovrebbe anche lasciare file poiché */guarda solo le cartelle.


1
Consiglio vivamente di testarlo per primo in quanto rmè principalmente destinato a rimuovere i file. Mentre */corrisponde solo alle directory, non ho idea di cosa faccia a livelli più profondi. Posso anche immaginare che funzioni solo su alcuni sistemi.
maaartinus,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.