Curinga `[!]` (ponto de exclamação entre colchetes) no bash

10

Eu me deparei com padrões esbugalhados e curingas e é particularmente interessante para mim [!].

Essa construção é semelhante à [!]construção, exceto que, em vez de corresponder a qualquer caractere dentro dos colchetes, ela corresponderá a qualquer caractere, desde que não esteja listada entre [e ].

rm myfile [!192]

O que eu acredito acima removerá todos os arquivos, exceto todos os arquivos com 192 em seu nome.

No entanto, estou preocupado com o uso adequado disso com uma extensão de arquivo e, em particular, com várias condições.

Qual seria a sintaxe apropriada em tal situação?

rm myfile [!.gif .csv. mp3] 

ou

rm myfile [!.gif !.csv !.mp3]

Minha preocupação é que o período possa ser extraviado e, portanto, qualquer arquivo com um .(que certamente seria um deles?) Seria manipulado quando estou procurando causar a manipulação de arquivos específicos.

Essa construção é semelhante à [ ]construção, exceto que, em vez de corresponder a qualquer caractere dentro dos colchetes, ela corresponderá a qualquer caractere, desde que não esteja listada entre [e ].

(citado em http://www.tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm )

Agora; para mim, isso sugere que um singular !é suficiente, e todos os valores a seguir são contidos no intervalo.

IronUhlan
fonte
5
como uma dica geral, o uso ls my-patternou echo my-commandpara verificar se o englobamento é feito da maneira que quiser, antes de executarrm
Wayne_Yux
Além disso, lembre-se de que muitos arquivos, se não a maioria, não possuem extensões. Extensões em sistemas Linux geralmente são irrelevantes e não são usadas para definir o tipo de arquivo pela maioria dos programas.
terdon
2
Note-se que a sua primeira citação é mal interpretado, agora parece dizer basicamente que [!]é semelhante, mas não como[!]
ilkkachu
2
Vale ressaltar que ^tem exatamente o mesmo significado que !neste contexto, portanto [^a]exclui aexatamente como [!a]faz: Se o primeiro caractere após o [for a! ou a ^, qualquer caractere não incluído é correspondido. ( man bash)
dessert
3
rm myfile [!192]irá remover myfile, bem como qualquer arquivo de um único caractere não nomeado 1, 9ou 2.
Kevin

Respostas:

16

Citação man 7 glob:

An  expression  "[!...]"  matches  a single character, namely any character
that is not matched by the expression obtained by removing the first '!' from it.
(Thus, "[!]a-]" matches any single character except ']', 'a' and '-'.)

A ênfase na parte de um único caractere aqui []não pode ser usada para seqüências de caracteres inteiras no Bash Pattern Matching.


bashA expansão de chaves pode ser usada para corresponder às strings, mas não há como (eu sei) negá-las. Por exemplo

1.{gif,csv,mp3}

é expandido para

1.gif 1.csv 1.mp3

não importa se esses arquivos existem.


Como o wchargin apontado shoptpode ser usado para habilitar operadores estendidos de correspondência de padrões, sendo um deles !(). Após a execução shopt -s extglob, por exemplo

1.!(gif|csv|mp3)

é expandido para

1.jpg 1.bmp 1.png

se esses arquivos existirem no diretório atual. Para mais, leia man bash(seção EXPANSÃO / Expansão do nome do caminho) e Expansão do nome do caminho (globbing) .


Para o que você está tentando fazer, eu sempre usaria find, o que oferece a negação e muito mais, por exemplo, da seguinte maneira:

find . -maxdepth 1 -type f ! -name "*.gif" -a ! -name "*.csv" -a ! -name "*.mp3"

Isso apenas lista as descobertas; se você deseja removê-las, basta anexar a -deleteopção ao final do comando. Como sempre com a remoção de ações: sempre verifique cuidadosamente primeiro .

findoferece uma tonelada de opções úteis muito bem explicadas por man find.

sobremesa
fonte
1
Observe que este findcomando é recursivo ( edite: como foi mostrado originalmente - eu posso manter esse comentário para obter informações adicionais relevantes, mas não essenciais, que ele transmite). Para localizar arquivos que residem apenas no diretório atual, mas não também em seus subdiretórios - e, portanto, exclua esses arquivos apenas se a -deleteação for adicionada - você pode adicionar -maxdepth 1imediatamente depois find .. Isso o alinharia com o efeito de globbing, pois os globs não são recursivos no Bash, a menos que a globstaropção de shell esteja ativada e **usada. O globo mostrado na pergunta não é recursivo em todas as conchas.
Eliah Kagan
2
"não há nenhuma maneira (que eu saiba) para negar-lhes", com shopt -s extglob, você pode usar echo 1.!(gif|csv|mp3), que imprime tudo 1.ext, onde extnão é gif, csvou mp3. (Consulte a subseção "Correspondência de padrões" de man bash.)
wchargin 10/11/17
10

Eu acho que você não entendeu o uso de colchetes. [abc]corresponde a um dos caracteres a, bou c. [!abc]corresponde um personagem que é não a , bou c. Então o comando

rm myfile [192]

irá remover myfilee os arquivos 1, 9e 2porque você tem um espaço entre o myfile e o suporte. Por outro lado, o comando

rm myfile[192]

irá remover os arquivos myfile1, myfile9e myfile2.

pLumo
fonte
verdade. Melhor usar find.
PLumo
Na verdade, [] é usado para uma série, um único caractere [!]
IronUhlan
1
@IronUhlan, ambos têm um intervalo ou uma lista de caracteres. Só que o outro é uma negação.
ilkkachu
7

Parênteses [ ]indicam uma classe de personagem. Qualquer caractere único dentro deles pode corresponder na posição especificada. assim

file[192]

corresponde a três nomes possíveis:

file1
file2
file9

Ele não corresponder file192, porque só lida com o caráter único após file.

Também podemos usar faixas entre parênteses. [0-9]corresponde a qualquer dígito na posição especificada. Para dois dígitos, precisamos [0-9][0-9]. Para um dígito seguido de uma letra maiúscula, poderíamos usar [0-9][A-Z]...

! indica negação.

file[!192]

corresponderá a arquivos que possuem um único caractere depois de fileexceto 1ou 9ou 2. Por exemplo, ele corresponderá file7e filese file%(e muitos outros), mas não file192 , porque isso possui dois caracteres extras.

Para o seu caso de uso, sugiro usar em findvez de [!]. Tem um notoperador.

Também é recursivo , o que significa que entra em todos os subdiretórios por padrão. Você pode limitá-lo ao diretório atual com -maxdepth 1, se é isso que você deseja. Os curingas do shell (globbing) não são recursivos (embora o globbing recursivo **possa ser ativado).

Supondo que você não queira evitar a exclusão de links simbólicos, podemos adicionar -type daos testes negados para evitar a exclusão de diretórios. Agradeço a Eliah Kagan por essa sugestão.

find /path -maxdepth 1 -not \( -type d -or -iname "*.gif" -or -iname "*.csv" -or -iname "*.mp3" \)

Se você vir o que deseja, poderá executar o comando novamente com -delete

find /path -maxdepth 1  -not \( -type d -or -iname "*.gif" -or -iname "*.csv" -or -iname "*.mp3" \) -delete
Zanna
fonte
2
E acho que você também pode fazer vários intervalos ao mesmo tempo. Por exemplo, hexadecimal dígitos pode ser[0-9a-fA-F]
Wilson
@ Zanna, sim! Eu estava realmente usando find :) Não perceber que tinha um operador "não" :)
IronUhlan