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?


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)

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

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' ./*
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.