Como excluir um ou vários sufixos de nome de arquivo ao pesquisar usando qualificadores glob?
Funciona bem, aqui combinamos o sufixo do nome do arquivo, não os excluímos:
print -rC1 **/*.(txt)(.)
print -rC1 **/*.(jpg|png)(.)
E não estamos tentando excluí-los, não funciona:
print -rC1 **/*.(^txt)(.)
print -rC1 **/*.(^jpg|^png)(.)
zsh 5.9 (x86_64-apple-darwin23.0)
Usar o
^x
padrão requer que aEXTENDED_GLOB
opção shell seja definida nozsh
shell:Depois disso, você poderá procurar qualquer arquivo normal não oculto cujo sufixo de nome de arquivo não seja
.foo
nem.bar
usando o padrãoObserve que
*.(^foo|^bar)
é muito diferente de^*.(foo|bar)
. O primeiro significa "qualquer nome de arquivo que contenha um.
que seja seguido por algo que não éfoo
ou algo que não ébar
" (essencialmente todos os arquivos com pelo menos um ponto, pois nada pode ser ambosfoo
ebar
ao mesmo tempo), enquanto o último significa "sem nome de arquivo com.foo
ou.bar
no final" .Se você não incluir o
./
prefixo, observe que é fundamental não esquecer a opção--
ou-
delimitada,print
caso contrário, isso o tornará uma vulnerabilidade de execução de comando arbitrária.Então, no seu caso:
Os qualificadores Glob não são realmente o problema aqui. Na verdade, eles fazem alguns dos exemplos funcionarem. Há três questões distintas aqui: como os qualificadores glob são analisados, a sintaxe dos operadores glob e as opções que os controlam.
Os parênteses são sempre aceitos para agrupamento em globs, embora haja várias circunstâncias em que eles são analisados como outra coisa antes de atingirem o estágio em que fazem parte de um glob (expressões aritméticas, qualificadores de glob que expandirei mais tarde, função definições, etc.). Em
*.(txt)(.)
, o último pedaço entre parênteses(.)
é analisado como qualificadores glob e(txt)
é um padrão que corresponde apenas ao texto exatotxt
.O padrão
*.(jpg|png)(.)
usa o operador glob|
que significa “ou”. A parte(jpg|png)
corresponde a ambosjpg
epng
.Por padrão,
^txt
corresponde apenas a si mesmo. O operador glob^
só é reconhecido quando a opçãoextended_glob
está definida. Esta opção está sempre definida nas funções de conclusão e nozmv
, mas está desativada por padrão. A única razão pela qual está desativado por padrão é a compatibilidade estrita com versões anteriores (não existia originalmente, mas existe há mais de 30 anos). Existem poucos casos práticos em que isso faz diferença (principalmente se você deseja passar um#
,^
ou~
em um argumento de comando sem citá-lo) e, mesmo quando isso acontece, uma barra invertida ou qualquer outra aspa corrige isso. Eu recomendo colocarsetopt extended_glob
o seu.zshrc
. Com esta opção ativada,^txt
corresponde a qualquer coisa, excetotxt
.O padrão
(^jpg|^png)
corresponde a qualquer coisa que não sejajpg
ou que não sejapng
. Qualquer string não é jpg ou não-png, então(^jpg|^png)
corresponde a tudo. Para corresponder a qualquer coisa que não sejajpg
nempng
, use^(jpg|png)
. Se houver mais depois da parte que você deseja negar, você precisará de dois parênteses posteriores, por exemplo,*.(^(jpg|png)).gz
para corresponderfoo.bar.gz
efoo.txt.gz
mas nãofoo.jpg.gz
oufoo.png.gz
.Agora vamos voltar a como os qualificadores glob são analisados. Por padrão, um grupo entre parênteses no final é analisado como qualificadores glob, exceto quando contém
|
parênteses aninhados ou~
(quandoextended_glob
está habilitado).Em
*.(txt)
,(txt)
é analisado como qualificadores glob (portanto,*.(txt)
corresponde a arquivos cujo nome termina com um ponto e que são executáveis e têm o sticky bit definido). O uso de parênteses duplos evita a análise do qualificador glob.*.(png|jpg)
não é interpretado como uso de qualificadores glob por causa do|
.*.(txt)(.)
já foi(.)
interpretado como qualificadores glob, portanto a(txt)
parte não pode ser qualificador glob.Você pode desativar a análise do qualificador glob desmarcando
bare_glob_qual
. Eu não recomendo isso, porque padrões ambíguos são raros e requerem digitação extra quando você deseja qualificadores glob. Observe que