Como remover todos os diretórios vazios em uma subárvore?


151

Como posso remover todos os diretórios vazios em uma subárvore? Eu usei algo como

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

mas preciso executar várias vezes para remover diretórios que contêm apenas diretórios vazios. Além disso, é bastante lento, especialmente no cygwin.


Consulte também emacs.stackexchange.com/q/12190/2264 para obter uma solução emacs.
Sean Allred

Respostas:


222

Combinando findopções e predicados GNU , este comando deve fazer o trabalho:

find . -type d -empty -delete
  • -type d restringe a diretórios
  • -empty restringe aos vazios
  • -delete remove cada diretório

A árvore é retirada das folhas sem a necessidade de especificar -depthcomo está implícita -delete.


2
-deletejá implica -depthpara que você não precise especificar isso manualmente.
Jamadagni

1
Obrigado, eu não percebi isso. Resposta atualizada.
Christophe Drevet-Droguet

11
Eu acrescentaria -mindepth 1aqui, para impedir a exclusão do próprio diretório inicial, se ele estiver vazio.
Greg Dubicki

2
Grande, mas não funciona em meus velhos anfitriões SunOS ...
dokaspar

2
!tem um significado especial para o shell. Você precisa escapar disso. Algo como: \! -name 'Completed'logo antes -deletedeve funcionar. Ou você apenas coloca um arquivo de marcador neste diretório.
Christophe Drevet-Droguet

53

Liste os diretórios profundamente aninhados primeiro.

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

(Observe que o redirecionamento se aplica ao findcomando como um todo, não apenas ao rmdir. Redirecionar apenas para rmdircausaria uma desaceleração significativa, pois você precisaria chamar um shell intermediário.)

Você pode evitar a execução rmdirem diretórios não vazios, passando o -emptypredicado para localizar. O GNU find testa o diretório quando está prestes a executar o comando, para que os diretórios que acabaram de ser esvaziados sejam escolhidos.

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

Outra maneira de acelerar seria agrupar as rmdirinvocações. É provável que ambos sejam visivelmente mais rápidos que o original, especialmente no Cygwin. Não espero muita diferença entre esses dois.

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

Qual método é mais rápido depende de quantos diretórios não vazios você possui. Você não pode combinar -emptycom métodos para agrupar chamadas, porque os diretórios que contêm apenas diretórios vazios não ficam vazios quando os findolha.

Outro método seria executar várias passagens. Se isso é mais rápido depende de muitas coisas, incluindo se toda a hierarquia de diretórios pode permanecer no cache do disco entre as findexecuções.

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

Como alternativa, use zsh. O qualificador glob F corresponde a diretórios não vazios, portanto, /^Fcorresponde a diretórios vazios. Diretórios que contêm apenas diretórios vazios não podem ser correspondidos tão facilmente.

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

(Isso termina quando rmdirrecebe uma linha de comando vazia.)


É isso aí. Em vez de 90 segundos, são necessários 0,90 s.
Maaartinus 01/03

@maaartinus: Estou curioso: você tem um conjunto de dados semelhante onde poderia tentar -p? Eu não teria pensado que isso faria diferença.
Gilles

3
@ maartinus - outras pequenas otimizações: a adição -emptydeve funcionar com esta (embora eu não tenha certeza exatamente quanto ela ganhará). E muito, muito trivialmente, já que você provavelmente não deseja remover ., use -mindepth 1.
mattdm

Não foi a remoção, mas o processo foi iniciado, o que levou quase o tempo todo. Eu havia ignorado o -depthargumento, o que torna rmdir -pinútil. Eu já mudei meu comentário. Os anos 90 foram minha tentativa original; não há nada de surpreendente aqui.
Maaartinus 01/03

2
Percebi podemos remover a rmdirchamada de comando por completo, pelo menos com GNU encontrar, com este comando:find . -depth -type d -empty -delete
Christophe Drevet-Droguet

6

Se você apenas colocar um -pno seu rmdir, isso funcionará de uma só vez. Não será bonito ou ideal, mas deve ter tudo. Isso diz ao rmdir para remover qualquer diretório-pai não vazio daquele que você está removendo.

Você pode economizar um pouco adicionando o -emptyteste a encontrar, para que ele não se incomode com diretórios não vazios.


3

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

é a resposta mais simples e compatível com esta pergunta.

Infelizmente, as outras respostas fornecidas aqui dependem de aprimoramentos específicos do fornecedor que não existem em todos os sistemas.


3
Esta resposta gera um erro para cada diretório que não pode ser excluído, o que pode ser menor que o desejável.
Willem van Ketwich 13/04/19

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

Veja como funciona:

  1. Liste recursivamente todos os diretórios, juntamente com sua profundidade
  2. Classificar por ordem decrescente da profundidade
  3. Filtrar apenas os caminhos do diretório
  4. Executar rmdirna lista, um por um

0

Eu uso esses aliases para findcomandos usados ​​com freqüência , especialmente quando eu limpo o espaço em disco usando o dupeguru , onde remover duplicatas pode resultar em muitos diretórios vazios.

Comentários lá dentro, .bashrcpara não esquecê-los mais tarde, quando precisar ajustá-los.

# 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 */comando funcionou facilmente para mim. rmdeve exigir -fa remoção forçada de diretórios com arquivos. rm -rdeve remover apenas diretórios vazios. Estou aberto a entender por que isso pode estar errado. Isso também deve deixar os arquivos, pois */apenas analisa as pastas.


1
Eu recomendo fortemente testá-lo completamente primeiro, pois rmse destina principalmente a remover arquivos. Embora */corresponda apenas aos diretórios, não tenho idéia do que ele faz em níveis mais profundos. Também posso imaginar que ele funciona apenas em alguns sistemas.
maaartinus 22/06
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.