No meu script de shell zsh, uso regexp-replace nname "_{2,}" "_"
com sucesso para reduzir vários "_"s a um único, mas quando tento ${nname//_{2,}/'_'}
zsh parece não corresponder ao padrão.
man zshexpn
é. não está claro. Ele menciona padrões globbing, mas evidentemente outras expressões regulares POSIX 1003.2, como em ${name//[^[:alnum:]]/"_"}
work OK no meu script.
Motivação
Eu estava procurando o equivalente de expansão de parâmetros de
regexp-replace nname "[^[:alnum:]]" "_"
regexp-replace nname "_{2,}" "_"
regexp-replace nname "_+$" ""
regexp-replace nname "^_+" ""
zsh
zsh --version
zsh 5.7.1 (x86_64-apple-darwin19.0)
${var//pattern/replacement}
está usando padrões curinga zsh parapattern
, os mesmos usados para geração de nome de arquivo, também conhecido como globbing, que são um superconjunto dossh
padrões curinga. A sintaxe também é afetada pelas opçõeskshglob
eextendedglob
. O${var//pattern/replacement}
vem do shell Korn inicialmente.Eu recomendaria habilitar
extendedglob
(set -o extendedglob
no seu~/.zshrc
) que oferece a maioria dos recursos (mais do que os EREs padrão) às custas de alguma incompatibilidade com versões anteriores em alguns casos de canto.Você vai encontrá-lo documentado em
info zsh 'filename generation'
.Uma folha de dicas para o mapeamento entre ERE e curingas zsh estendidos:
Os padrão
sh
:.
->?
.*
->*
[...]
->[...]
extensões zsh:
*
->#
+
->##
{x,y}
->(#cx,y)
(...|...)
->(...|...)
alguns recursos extras não disponíveis em EREs padrão:
^pattern
(negação)x~y
(exceto)<12-234>
corresponder a intervalos de números decimais(#i)
correspondência sem distinção entre maiúsculas e minúsculas(#a2)
correspondência aproximada permitindo até 2 erros.Se os padrões curinga são ancorados no início ou no final do assunto depende de qual operador é usado.
case
patterns[[ string = pattern ]]
e${var:#pattern}
são ancorados em ambos (f*.txt
corresponderão emfoo.txt
, nãoXfoo.txtY
)${var#pattern}
e${var##pattern}
estão ancorados no início${var%pattern)
e${var%%pattern}
estão ancorados no final${var/pattern/repl}
e${var//pattern/repl}
não estão ancorados, mas podem ser feitos com${var/#pattern}
(início) ou${var/%pattern}
(fim).(#s)
e(#e)
também podem ser usados como equivalentes de^
/$
(ERE) ou\A
/\z
(PCRE).Se os operadores repetidos (
#
,##
,*
,(#cx,y)
,<x-y>
) são gananciosos também depende do operador (ganancioso com##
,%%
,//
,/
não com#
,%
), que pode ser alterado com oS
sinalizador de expansão do parâmetro.Então para seus exemplos:
regexp-replace nname "[^[:alnum:]]" "_"
:${var//[^[:alnum:]]/_}
regexp-replace nname "_{2,}" "_"
:${var//_(#c2,)/_}
regexp-replace nname "_+$" ""
:${var%%_#}
ou${var/%_#}
(aqui usando#
para o*
equivalente, você pode usar##
para um+
equivalente, mas isso não fará diferença neste caso).regexp-replace nname "^_+" ""
:${var##_#}
ou${var/#_#}
Aqui, você pode combiná-los com
${${${var//[^[:alnum:]]##/_}#_}%_}
(converter sequências de não-alnums_
e remover um eventual início ou final_
).Outra abordagem poderia ser extrair todas as sequências de alnums e juntá-las com
_
, usando este hack:regexp-replace
em si é uma função autocarregável que chama[[ $var =~ pattern ]]
em um loop. Observe que, como resultado, ele não funciona corretamente com os^
operadores âncora ou limite de palavra ou look-behind (se estiver usando arematchpcre
opção) :(no primeiro exemplo,
^a
é correspondido por sua vez contraaaab
,aab
,ab
,b
nesse loop).