Exclua todas as pastas dentro de uma pasta, exceto uma com nome específico

17

Eu preciso excluir todas as pastas dentro de uma pasta usando um script diário. A pasta para esse dia precisa ser deixada.

A pasta 'myfolder' possui 3 subpastas: 'test1', 'test2' e 'test3' Preciso excluir tudo, exceto 'test2'.

Estou tentando combinar o nome exato aqui:

find /home/myfolder -type d ! -name 'test2' | xargs rm -rf

OU

find /home/myfolder -type d ! -name 'test2' -delete

Este comando sempre tenta excluir a pasta principal 'myfolder' também! Há alguma maneira de evitar isto ?

Riju Mahna
fonte
6
No Unix e Linux, chamamos esses "diretórios", não "pastas".
tchrist
1
Dependendo do seu shell, pode ser necessário citar esse !operador: \!ou '!'.
21418 Toby Speight

Respostas:

32

Isso excluirá todas as pastas internas, ./myfolderexceto que ./myfolder/test2e todo o seu conteúdo será preservado:

find ./myfolder -mindepth 1 ! -regex '^./myfolder/test2\(/.*\)?' -delete

Como funciona

  • find inicia um comando find.
  • ./myfolderdiz ao find para começar com o diretório ./myfoldere seu conteúdo.

  • -mindepth 1 para não corresponder a ./myfoldersi próprio, apenas os arquivos e diretórios abaixo dele.

  • ! -regex '^./myfolder/test2\(/.*\)?' diz ao find para excluir ( !) qualquer arquivo ou diretório que corresponda à expressão regular ^./myfolder/test2\(/.*\)?. ^corresponde ao início do nome do caminho. A expressão (/.*\)?corresponde (a) a uma barra seguida por qualquer coisa ou (b) nada.

  • -delete diz ao find para excluir os arquivos correspondentes (ou seja, não excluídos).

Exemplo

Considere uma estrutura de diretório que se parece;

$ find ./myfolder
./myfolder
./myfolder/test1
./myfolder/test1/dir1
./myfolder/test1/dir1/test2
./myfolder/test1/dir1/test2/file4
./myfolder/test1/file1
./myfolder/test3
./myfolder/test3/file3
./myfolder/test2
./myfolder/test2/file2
./myfolder/test2/dir2

Podemos executar o comando find (sem -delete) para ver o que ele corresponde:

$ find ./myfolder -mindepth 1 ! -regex '^./myfolder/test2\(/.*\)?'
./myfolder/test1
./myfolder/test1/dir1
./myfolder/test1/dir1/test2
./myfolder/test1/dir1/test2/file4
./myfolder/test1/file1
./myfolder/test3
./myfolder/test3/file3

Podemos verificar se isso funcionou olhando os arquivos que permanecem:

$ find ./myfolder
./myfolder
./myfolder/test2
./myfolder/test2/file2
./myfolder/test2/dir2
John1024
fonte
1
Alternativa para -prunedeixar test2/*/subdiretórios em paz: retorne rm -re adicione -maxdepth 1.
Toby Speight
@Isaac OK. Feito. (Além disso, uma para o seu excelente resposta.)
John1024
Excelente trabalho !, mas desculpe: isso removerá todos os arquivos internos ./myfolder. Você precisa de um (IMvhO) ausente apenas-type d para diretórios .
Isaac Isaac
Ok, isso deve funcionar como você deseja: #find ./myfolder -depth -mindepth 1 -maxdepth 1 -type d ! -regex '^./myfolder/test2\(/.*\)?'
Isaac
10

Usando o bash :

shopt -s extglob
rm -r myfolder/!(test2)/

Exemplo:

$ tree myfolder/
myfolder/
├── test1
│   └── file1
├── test2
│   └── file2
└── test3
    └── file3

$ echo rm -r myfolder/!(test2)
rm -r myfolder/test1 myfolder/test3
$ rm -r myfolder/!(test2)
$ tree myfolder/
myfolder/
└── test2
    └── file2

1 directory, 1 file
Jeff Schaller
fonte
5

tl; dr

find ./myfolder -mindepth 1 -maxdepth 1 -type d -not -name test2 \
     -exec echo rm -rf '{}' \;

Remova o eco se estiver satisfeito com a lista de arquivos.


O uso -mindepth 1garantirá que o diretório superior não esteja selecionado.

$ find ./myfolder -mindepth 1 -type d
./myfolder/test2
./myfolder/test2/one
./myfolder/test2/two
./myfolder/test
./myfolder/test/a1
./myfolder/test/a1/a2
./myfolder/test/a1/a2/a3

Mas -not -name test2será não evitar subdirs dentro test2:

$ find ./myfolder -mindepth 1 -type d -not -name 'test2'
./myfolder/test2/one
./myfolder/test2/two
./myfolder/test
./myfolder/test/a1
./myfolder/test/a1/a2
./myfolder/test/a1/a2/a3

Para fazer isso, você precisa de algo como a ameixa:

$ find ./myfolder -mindepth 1 -name test2 -prune -o -type d -print
./myfolder/test
./myfolder/test/a1
./myfolder/test/a1/a2
./myfolder/test/a1/a2/a3

Mas não use delete, como implica depthe isso começará a ser apagado do caminho mais longo:

$ find ./myfolder -depth -mindepth 1 -name test2 -prune -o -type d -print
./myfolder/test/a1/a2/a3
./myfolder/test/a1/a2
./myfolder/test/a1
./myfolder/test

Ou use rm -rf(remova o echose você deseja realmente apagar):

$ find ./myfolder -mindepth 1 -name test2 -prune -o -type d -exec echo rm -rf '{}' \;
rm -rf ./myfolder/test
rm -rf ./myfolder/test/a1
rm -rf ./myfolder/test/a1/a2
rm -rf ./myfolder/test/a1/a2/a3

Ou use tambémmaxdepth se tudo o que você precisa é excluir diretórios (e tudo o que estiver dentro) (remova o echopara realmente apagar):

$ find ./myfolder -mindepth 1 -maxdepth 1 -type d -not -name test2 -exec echo rm -rf '{}' \;
rm -rf ./myfolder/test

A -deleteainda falhará se o diretório não estiver vazio:

$ find ./myfolder -mindepth 1 -maxdepth 1 -type d -not -name test2 -delete
find: cannot delete ‘./myfolder/test’: Directory not empty
Isaac
fonte
2

Se você estiver usando o zsh, poderá:

setopt extended_glob # if you don't have it enabled

rm -rf myfolder/^test2
JoL
fonte
0

Testado com o comando abaixo e funcionou bem

find  /home/myfolder -maxdepth 1 -type d ! -iname test2 -exec rm -rvf {} \;
Praveen Kumar BS
fonte
Você encontrou o mesmo problema que o OP; listar / home / pasta na linha de comando (sem a crítica -mindepth 1) faz com que o diretório superior corresponda a todos os critérios (é um diretório e não é chamado "test2") e, portanto, é excluído.
Jeff Schaller