Como excluir diretórios com base na saída `find`?

148

Emito o seguinte comando para encontrar os diretórios .svn:

find . -name ".svn"

Isso me dá os seguintes resultados:

./toto/.svn
./toto/titi/.svn
./toto/tata/.svn

Como eu poderia processar todas essas linhas rm -frpara excluir os diretórios e seu conteúdo?

Arnaud
fonte
3
A localização do GNU tem a -deleteopção.
Marco
5
Ou você pode adicionar -exec rm -r "{}" \;ao final da descoberta - tenha cuidado ao usar rm -r! :)
Drav Sloan
10
@Marco A opção de exclusão não parece funcionar em diretórios.
Arnaud
@SuperChafouin Funciona perfeitamente bem aqui em arquivos e diretórios. O ponto é que ele exclui apenas diretórios vazios e, quando você o especifica -name ".svn", corresponde apenas ao .svndiretório em si e não aos arquivos localizados no .svndiretório.
Marco
1
@SuperChafouin, mas não funcionará para caminhos com espaços neles (portanto, use -execcom citado "{}").
Drav Sloan

Respostas:

195

O Find pode executar argumentos com a -execopção para cada correspondência encontrada. É um mecanismo recomendado porque você pode manipular caminhos com espaços / novas linhas e outros caracteres neles corretamente. Você precisará excluir o conteúdo do diretório antes de poder removê-lo, portanto, use -rcom o rmcomando para conseguir isso.

Para o seu exemplo, você pode emitir:

find . -name ".svn" -exec rm -r "{}" \;

Você também pode dizer ao find para encontrar apenas os diretórios denominados .svn adicionando uma -type dverificação:

find . -name ".svn" -type d -exec rm -r "{}" \;

Aviso Use rm -rcom cuidado para excluir a pasta e todo o seu conteúdo.

Se você deseja excluir apenas diretórios vazios e diretórios que contêm apenas diretórios vazios, o find pode fazer isso sozinho com -deletee -empty:

find . -name ".svn" -type d -empty -delete
Drav Sloan
fonte
22
Tenho visto conselhos para sempre correr -type atrás -name nos comandos find, pois as chamadas statpara obter o tipo são caras. Eu mesmo tentei em um monte de arquivos bastante grande e parece verdade: a execução find . -name 'foo' -type dlevou 19 segundos e find . -type d -name 'foo'32 segundos. Portanto, cerca de 50% mais tempo para executar -typeprimeiro.
spinup
6
uso este comando há anos, mas agora no Mac recebo erros dizendo que esses diretórios não existem. Mesmo que ele os exclua. Eu nunca vi mensagens antes.
chovy
1
O mesmo que @chovy. Existe uma maneira de se livrar dessas mensagens?
Clément
2
@chovy @ clément Isso porque você findquer ver nessa pasta outras correspondências, enquanto remove a pasta ao mesmo tempo. ~ Eu não sei ainda como consertar isso ~ correção sujo:.find . -name "folder-to-delete" -print0 | xargs -r0 -- rm -r
Charlie
5
@chovy @ Clément Eu acho que as -depthcorreções argumento isto:find . -depth -name ".svn" -type d -exec rm -r "{}" \;
gimboland
67

Aqui está um portátil ainda mais rápido que o modo de resposta aceito.

O uso +de um ponto e vírgula como findterminador de comando está otimizando o uso da CPU. Isso pode ser significativo se você tiver muitos .svnsubdiretórios:

find . -name .svn -type d -exec rm -rf {} +

Note também que você nunca 1 necessidade de citar as chaves aqui.

1 A menos que você use o fishshell.

jlliagre
fonte
5
Qual é a diferença entre + e ponto e vírgula? Por que não usamos aparelho encaracolado?
Shicheng Guo
1
@ShichengGuo Com o ponto e vírgula, haverá um comando rm por diretório encontrado, com o comando + um único rm processará todos os diretórios encontrados (ou pelo menos um número muito grande deles). Não entendi sua segunda pergunta, encaracolado chaves são usadas aqui.
Jlliagre
Acho que @ShichengGuo significa por que não precisamos citar as chaves aqui (@jlliagre escreveu que nunca precisamos citá-las). Não consigo encontrar uma referência agora, mas entendo que é porque find escapará automaticamente dos caminhos substituídos por {}.
Quinn Comendant
A resposta e a pergunta não estão certas sobre o que + faz. Se muitos arquivos forem encontrados, ';' daria erro 'linha de comando muito longa'. + divide os arquivos encontrados em lotes com comprimento inferior ao máximo permitido da linha de comando e executa o comando para cada lote.
gaoithe
1
@gaoithe eu fiz o rollback que você editou, que substituiu uma declaração correta por uma incorreta. Usando + não reduzir o uso de CPU, usando ; não levar a um comando muito longa erro.
Jlliagre 22/03/19
27

Suponha que você esteja usando o gnu find , você pode usar a -deleteopção:

find . -name test -delete

o que é mais fácil de lembrar.

Chun Yang
fonte
Considere expandir sua postagem com uma explicação do comando (ou documentação para fazer backup da sua solução). Frequentemente, uma (ou duas) respostas de linha não são as mais esclarecedoras.
precisa saber é o seguinte
94
Isso não funciona em diretórios não vazios.
Belacqua
2
também funciona no Mac OS X
desenhe
Isso não funciona para pasta não vazio, mas é a solução mais fácil e mais seguro
RousseauAlexandre
A ordem de opções é muito importante, encontrar irá executá-los em ordem para, -delete tem que ser o último
Jose Ignacio Centeno
13

No meu computador quando uso:

find . \( -name dirname -type d \) -exec rm -r '{}' ';'

Os diretórios são excluídos, mas eu recebo o erro:

find: ‘./dirname’: No such file or directory

para cada diretório.

Como meus diretórios não estão vazios, a opção -delete não funcionará para mim. Encontrei o motivo desse comportamento aqui :

  1. find obtém (não necessariamente) a primeira entrada no diretório ./. isso seria, por exemplo, dir.1 /
  2. compara-o ao padrão 'dir.?'. isso combina? sim.
  3. find executa "rm -r dir.1".
  4. find tenta digitar dir.1 / para encontrar o padrão dentro do diretório não sabe nada sobre o comando exec.
  5. não encontra dir.1 / anymore. retorna ENOENT (veja a saída do strace)

Eu usei isso para solucionar o problema:

rm -r `find . -name dirname -type d`

Lembre-se de que o find ainda tentará recursar nos diretórios denominados dirname, o que não é realmente necessário e levará mais tempo. Dependendo da sua estrutura de diretórios, você poderá contornar isso com a --depthopção find. Além disso, se você tiver uma estrutura de diretórios como dirname / foo / dirname, receberá rm "Não existe esse arquivo ou diretório". Para suprimir os erros, você pode redirecionar o stderr para / dev / null ou usar o -fsinalizador (force) com rm.

Samuel
fonte
2
Má idéia: nome de arquivo com espaços fará com que todos os tipos de problemas horríveis
Clément
2
Para futuros leitores: find . -name "to-delete" -print0 | xargs -r0 -- rm -ré uma versão failproof que não falhar em espaços
Charlie
3
Consulte unix.stackexchange.com/a/115869/8257 que você precisa adicionar -prune.
Mapio
1
Adicione -prunepara evitar o erro "Esse arquivo ou diretório não existe".
Julian Carsique
12

Uma maneira mais rápida de fazer isso é:

find . -name ".svn" -type d -prune -exec rm -rf '{}' '+'

Caso você tenha ".svn" dentro de outro ".svn".

Chang Yu-heng
fonte
Isso não é muito útil se você deseja encontrar os diretórios a serem excluídos com os subdiretórios.
Alexis Wilke
5

Solução específica do Bash:

shopt -s globstar
rm -r **/.svn
shopt -u globstar #optional. this will disable globstar expansion
pressa
fonte
3
Observe que, para a expansão de globs na linha de comando que corresponde a muitos arquivos, há um limite para o número de arquivos que você pode corresponder a esse mecanismo. bash: /bin/rm: Argument list too long
Ultrapassar
1
O @DravSloan está correto, mas esse limite está nas centenas de milhares de arquivos. É algo a ter em mente, mas provavelmente não será um problema para a maioria das pessoas.
evilsoup
4

Descobri que a -deleteação funciona bem com o -pathteste. Por exemplo, o seguinte deve funcionar no problema de pôsteres originais:

find . -path '*/.svn*' -delete
Magnus
fonte
Você tem certeza? -deleteimplica -depth, e com certeza exclui diretórios não vazios no meu sistema.
Magnus
Testado novamente e funciona. Talvez tenha sido apenas um erro de digitação que eu corrigi.
Kenorb # 5/18
Eu também tentei essa abordagem e funcionou - os diretórios foram excluídos.
11446 Allan