Excluir de * na linha de comando

14

Existem muitas situações em que o uso de a *é praticamente inevitável - por exemplo, rm -rf *em uma pasta que contém milhares de subpastas e arquivos.

Mas e se você quiser excluir apenas um ou dois arquivos ou pastas do rmcomando? Pesquisei no Google e só encontrei soluções bastante complicadas, como find . -depth -not \( -name 'one' -o -name 'two' \ -o -name 'three' \) -exec rm {} \;as mencionadas aqui .

Existe a possibilidade de fazer isso de uma maneira mais fácil - sem esse desvio find? Por exemplo, rm -rf --exclude='one' --exclude='two' --exclude='three' *como no rsync ou apenas rm -rf -e 'one','two','three' *?

Talvez até mesmo uma possibilidade geral de excluir coisas de *(para que outros comandos como cp, mv... não tem que implementar a sua própria)? Algo mais *{'one','two','three'}ou menos?

David
fonte
3
Se você deseja manter apenas um ou dois arquivos, a maneira mais simples e fácil seria movê-los para outro local, limpe todos os arquivos restantes e mova os que você guardou. Se você deseja manter muito mais arquivos, eu usaria findcom a --deleteopção (não é necessário executar rmpara cada arquivo. Isso é desnecessário).
Hennes
Isso seria uma possibilidade, mas é quase tão elaborada quanto a que mencionei. Eu sei que poderia combinar os comandos para algo como mv -t /tmp one two three && rm -rf * && mv -t . /tmp/one /tmp/two /tmp/three, mas eu preferiria uma solução que desse a possibilidade de excluir explicitamente algo de *. Certamente haverá situações em que mover ou copiar os arquivos para outro destino não será uma opção.
David
2
É por isso que não a publiquei como resposta. Apenas como uma maneira menos complexa de fazer as coisas (três comandos muito simples versus um pouco mais complexo). Eu odeio complexidade em combinação com rm.
Hennes

Respostas:

33

Para Bash há uma opção de shell chamado extglobque está desativada por padrão para manter a compatibilidade com a sintaxe shell padrão. Extglob complementa a sintaxe pelos operadores adicionais, como !(), ?(), @()e alguns mais.

Para ligar extglob, digite shopt -s extglob. Para mantê-lo ativado para o tipo de usuário atual echo 'shopt -s extglob' >> ~/.bashrc.

Para o exemplo rm: With extglobyou can use

rm -rf !(one|two|three)
choroba
fonte
Parece uma boa solução, mas também não quero ativar e desativar toda vez que entrar em uma situação como a descrita.
David
3
@ David: você pode colocar a shoptcoisa no seu ~/.bashrc, não pode doer.
enzotib
Se eu acertar, a opção extglob não altera o efeito *, mas adiciona indicadores semelhantes (como o !) que possuem funções adicionais? Nesse caso, seria uma solução adorável.
David
1
@ David: Exatamente.
choroba
1
@ David Para manter a compatibilidade com a sintaxe padrão do shell . Provavelmente, há outras situações em que o comportamento pode mudar.
Gerrit
4

Criei, exceto exatamente para isso (sry que ainda não consegui adicionar documentação).

Você tem uma pasta como a seguinte:

$ ls
a b c d

A digitação except b dfornecerá a você aec

$ except b d
ac

Agora você pode canalizar isso para rm.

except b d | xargs -0 rm

Isso pode ser difícil de lembrar, então por que não criar um script em cima de exceto, chamado rm-except:

#!/usr/bin/env bash

except "$@" | xargs -0 rm

Da mesma forma fácil é ls-except:

#!/usr/bin/env bash

except "$@" | xargs -0 ls
Método auxiliar
fonte
3
Isso é bom, mas não é realmente necessário quando você sabe sobre extglob - askubuntu.com/a/329233/138369
David
1

Como uma alternativa ao extglob (embora essa seja uma resposta muito boa e todos devam ter shopt -s extglob globstarem seu .bashrc), você pode usar a variável global $ GLOBIGNORE . Digamos que você deseja obter todos os arquivos, exceto 'foo.txt' e 'bar baz.txt':

GLOBIGNORE=foo.txt:'bar baz.txt'

... no entanto, isso ativará a opção shell dotglob, o que significa que *corresponderá aos arquivos que começam com um ponto (que normalmente estão ocultos). Então você realmente precisa de dois comandos:

GLOBIGNORE=foo.txt:'bar baz.txt'
shopt -u dotglob

Como essa é uma variável global, ela afetará todos os globos que você usar até que $ GLOBSTAR seja desmarcado, efetuando logout ou com

GLOBIGNORE=

Também funcionará apenas nas cadeias literais passadas para a variável. Você pode entender o que quero dizer definindo $ GLOBIGNORE e observando a diferença entre estes comandos:

printf '%s\n' *
printf '%s\n' ./*
evilsoup
fonte