'[!]' (ponto de exclamação entre parênteses) curinga no bash

10

Eu me deparei com padrões globbing e curingas e de particular interesse para mim é [!] .

  

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

rm myfile [!192]

Acima eu acredito que irá remover qualquer um dos arquivos, exceto todos os arquivos que tiverem 192 em seu nome.

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

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

rm myfile [!.gif .csv. mp3] 

ou

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

Minha preocupação é que o período possa ser perdido, e assim qualquer arquivo com . (o que seria algum deles certamente?) seria então manipulado, quando eu estou tentando causar manipulação de arquivos específicos.

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

(citado em link )

Agora; para mim, que sugere, então, um singular ! é suficiente e todos os valores após estão contidos dentro do intervalo.

    
por IronUhlan 10.11.2017 / 09:55

3 respostas

16

Citando 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 '-'.)

Ênfase na parte de caractere único aqui, [] não pode ser usado para strings inteiras no Bash Pattern Matching.

A Expansão de Brace do

bash pode ser usada para combinar strings, mas não há como (Eu sei) para negá-los. Por exemplo,

1.{gif,csv,mp3}

é expandido para

1.gif 1.csv 1.mp3

não importa se esses arquivos existem.

Como o wchargin apontou, shopt pode ser usado para habilitar operadores estendidos de correspondência de padrões, sendo um deles !() . Depois de executar 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 informações, leia man bash (seção EXPANSÃO / Expansão do nome do caminho) e Expansão do nome do tráfego (globbing) .

Para o que você está tentando fazer, eu sempre uso find , 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ê quiser removê-las basta anexar a opção -delete ao final do comando. Como sempre com a remoção de ações: Sempre verifique cuidadosamente primeiro .

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

    
por dessert 10.11.2017 / 10:11
10

Acho que você entendeu mal o uso de colchetes. [abc] corresponde a um dos caracteres a , b ou c . [!abc] corresponde a um caractere que não é a , b ou c . Então o comando

rm myfile [192]

removerá myfile e os arquivos 1 , 9 e 2 porque você tem um espaço entre o arquivo myfile e o colchete. Em contraste, o comando

rm myfile[192]

removerá os arquivos myfile1 , myfile9 e myfile2 .

    
por RoVo 10.11.2017 / 10:07
7

Os colchetes [ ] denotam uma classe de caracteres. Qualquer personagem individual dentro deles pode corresponder na posição dada. Então

file[192]

corresponde a três nomes possíveis:

file1
file2
file9

Não não corresponde a file192 , porque lida apenas com o caractere único após file .

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

! indica negação.

file[!192]

corresponderá aos arquivos que tiverem um único caractere após file , exceto 1 ou 9 ou 2 . Por exemplo, ele corresponderá a file7 e files e file% (e muitos outros), mas não file192 , porque isso tem dois caracteres extras.

Para o seu caso de uso, sugiro usar find em vez de [!] . Tem um operador not .

Ele também é recursivo , o que significa que ele 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 da shell (globbing) não são recursivos (embora a globalização recursiva com ** possa ser ativada).

Supondo que você não queira evitar a exclusão de links simbólicos, podemos adicionar -type d aos testes negados para evitar a exclusão de diretórios. Obrigado 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
    
por Zanna 10.11.2017 / 10:16