TL; DR: a melhor maneira é usar em -exec rm
vez de -delete
.
find a \( -name b -prune \) -o -type f -exec rm {} +
Explicação:
Por que encontrar reclamar quando você tentar usar -delete
com -prune
?
Resposta curta: porque -delete
implica -depth
e -depth
torna -prune
ineficaz.
Antes de chegarmos à resposta longa, observe primeiro o comportamento de encontrar com e sem -depth
:
$ find foo/
foo/
foo/f1
foo/bar
foo/bar/b2
foo/bar/b1
foo/f2
Não há garantia sobre o pedido em um único diretório. Mas há uma garantia de que um diretório seja processado antes de seu conteúdo. Anote foo/
antes de qualquer foo/*
e foo/bar
antes de qualquer foo/bar/*
.
Isso pode ser revertido com -depth
.
$ find foo/ -depth
foo/f2
foo/bar/b2
foo/bar/b1
foo/bar
foo/f1
foo/
Observe que agora todos foo/*
aparecem antes foo/
. O mesmo com foo/bar
.
Resposta mais longa:
-prune
impede que a localização desça para um diretório. Em outras palavras, -prune
pula o conteúdo do diretório. No seu caso, -name b -prune
impede que a localização desça para qualquer diretório com o nome b
.
-depth
faz encontrar para processar o conteúdo de um diretório antes do próprio diretório. Isso significa que no momento em que a find processa a entrada do diretório, b
seu conteúdo já foi processado. Assim -prune
é ineficaz com -depth
efeito.
-delete
implica -depth
que ele possa excluir os arquivos primeiro e depois o diretório vazio. -delete
se recusa a excluir diretórios não vazios. Eu acho que seria possível adicionar uma opção para forçar -delete
a exclusão de diretórios não vazios e / ou impedir -delete
a implicação -depth
. Mas isso é outra história.
Há outra maneira de conseguir o que você deseja:
find a -not -path "*/b*" -type f -delete
Isso pode ou não ser mais fácil de lembrar.
Este comando ainda desce para o diretório b
e processa todos os arquivos nele apenas para -not
rejeitá-los. Isso pode ser um problema de desempenho se o diretório b
for enorme.
-path
funciona de forma diferente do que -name
. -name
corresponde apenas ao nome (do arquivo ou diretório) enquanto -path
corresponde ao caminho inteiro. Por exemplo, observe o caminho /home/lesmana/foo/bar
. -name -bar
corresponderá porque o nome é bar
. -path "*/foo*"
corresponderá porque a cadeia /foo
está no caminho. -path
tem alguns meandros que você deve entender antes de usá-lo. Leia a página do manual find
para mais detalhes.
Cuidado que isso não é 100% infalível. Há chances de "falsos positivos". A maneira como o comando é escrito acima irá pular qualquer arquivo que possua qualquer diretório pai cujo nome esteja começando com b
(positivo). Mas também ignorará qualquer arquivo cujo nome esteja começando, b
independentemente da posição na árvore (falso positivo). Isso pode ser corrigido escrevendo uma expressão melhor que "*/b*"
. Isso é deixado como um exercício para o leitor.
Presumo que você usou a
e b
como espaços reservados e os nomes reais são mais parecidos com allosaurus
e brachiosaurus
. Se você colocar brachiosaurus
no lugar b
, a quantidade de falsos positivos será drasticamente reduzida.
Pelo menos os falsos positivos será não excluído, por isso vai ser não tão trágico. Além disso, você pode verificar se há falsos positivos executando primeiro o comando sem -delete
(mas lembre-se de colocar o implícito -depth
) e examine a saída.
find a -not -path "*/b*" -type f -depth
a
menosa/b
?