Estou trabalhando em um prompt ZSH personalizado e quero repetir um caractere n
em uma string (como espaços para preenchimento). Esta string é impressa com print -rP
(o -r
sinalizador ignora as convenções de escape de eco e o -P
sinalizador executa expansões de prompt).
Tenho um código funcionando usando algum tipo de substituição de string, mas não sei como funciona. Por alguma razão, tenho que multiplicar o número de caracteres que desejo imprimir por dois, o que parece um hack.
$ n=3
$ c='a'
$ print -rP "${(l:$n::$c:)}" # why doesn't this work?
ca
$ print -rP "${(l:(( $n * 2 ))::$c:)}" # but this does?
aaa
Então, 1) por que isso funciona quando multiplicado por dois e 2) qual é a sintaxe correta para repetir um caractere dentro de uma string?
Responder1
1) por que isso funciona quando multiplicado por dois,
A expansão "${(l:3::$c:)}"
se expande para c$c
enquanto "${(l:3*2::$c:)}"
se expande para $c$c$c
. Se a opção PROMPT_SUBST
estiver definida e esta sequência for usada como parte de uma sequência de prompt, ela será avaliada para expansão de parâmetros, substituição de comandos e expansão aritmética. Então se c=a
, então c$c
se torna ca
e $c$c$c
se torna aaa
.
Teste com XTRACE
conjunto:
$ n=3 c=a zsh -o PROMPT_SUBST -xc 'print -rP -- "${(l:n::$c:)}"'
+zsh:1> print -rP -- 'c$c'
ca
$ n=3 c=a zsh -o PROMPT_SUBST -xc 'print -rP -- "${(l:n*2::$c:)}"'
+zsh:1> print -rP -- '$c$c$c'
aaa
e 2) qual é a sintaxe correta para repetir um caractere dentro de uma string?
O l
sinalizador de expansão de parâmetro pode ser usado da mesma maneira que você já está usando. No entanto, o p
sinalizador deve ser usado para permitir $c
que o argumento da string seja considerado o valor da variável c
antes do preenchimento (obrigado @StéphaneChazelas por apontar isso).
$ n=3 c=a zsh -xc 'print -r -- "${(pl:n::$c:)}"'
+zsh:1> print -r -- aaa
aaa
Observe que esta é a única forma de expansão de parâmetros aceita por esta construção, conforme man zshexpn
(na seção sobre Flags de Expansão de Parâmetros):
p
Reconheça as mesmas sequências de escape que osAlternativamente, com esta opção os argumentos de string podem estar na forma
$var
em que o valor da variável é substituído. Observe que este formulário é rigoroso; o argumento string não sofre expansão de parâmetro geral.
Responder2
Usando sua notação original, você pode conseguir o que deseja, usando p
pelo menoscomeçodos sinalizadores de parâmetro
print "${(pl:$n::$c:)}"
Para mais informações e alguns outros exemplos úteis, consulte a seção5.4.6: Ainda mais sinalizadores de parâmetrosemCapítulo 5: Substituiçõesdo guia zsh. Menciona maiúsculas P
, mas não p
:
Aqui estão alguns outros sinalizadores de parâmetros; Estou repetindo alguns deles. Uma opção muito útil é t
informar o tipo de parâmetro. Isso também apareceu no capítulo 3. O uso mais comum é testar o tipo básico do parâmetro antes de tentar usá-lo:
if [[ ${(t)myparam} != *assoc* ]]; then
# $myparam is not an associative array. Do something about it.
fi
Outro tipo muito útil é o preenchimento esquerdo ou direito de uma string, até um comprimento especificado e, opcionalmente, com uma string de preenchimento especificada para usar em vez de espaço; você pode até especificar uma string única para ir ao lado da string em questão.
foo='abcdefghij'
for (( i = 1; i <= 10; i++ )); do
goo=${foo[1,$i]}
print ${(l:10::X::Y:)goo} ${(r:10::X::Y:)goo}
done
imprime o bastante bonito:
XXXXXXXXYa aYXXXXXXXX
XXXXXXXYab abYXXXXXXX
XXXXXXYabc abcYXXXXXX
XXXXXYabcd abcdYXXXXX
XXXXYabcde abcdeYXXXX
XXXYabcdef abcdefYXXX
XXYabcdefg abcdefgYXX
XYabcdefgh abcdefghYX
Yabcdefghi abcdefghiY
abcdefghij abcdefghij
Observe que esses dois pontos (que podem ser outros caracteres, como expliquei para os sinalizadores ( s
) e ( j
)) sempre ocorrem em pares antes e depois do argumento, de modo que, com três argumentos, os dois pontos intermediários são duplicados. Você pode perder parte :Y:
e :X:
parte e ver o que acontece. As strings de preenchimento não precisam ter caracteres únicos; se eles não couberem um número exato de vezes no espaço de preenchimento, a última repetição será truncada na extremidade mais distante do argumento do parâmetro que está sendo inserido.
Dois parâmetros informam ao shell que você deseja que algo especial seja feito com o valor da substituição do parâmetro. A P
flag ( ) força o valor a ser tratado como um nome de parâmetro, para que você obtenha o efeito de uma substituição dupla:
% final=string
% intermediate=final
% print ${(P)intermediate}
string
É um pouco como se $intermediate
fosse o que em ksh é chamado de a nameref
, um parâmetro marcado como referência a outro parâmetro. Zsh pode eventualmente ter isso também; há lugares onde eles são muito mais convenientes do que a (P)
bandeira.
Um sinalizador mais poderoso é ( e
), que força o valor a ser verificado novamente para todas as formas de substituição de uma única palavra. Por exemplo,
% foo='$(print $ZSH_VERSION)'
% print ${(e)foo}
4.0.2
fez com que o valor de $foo
fosse reexaminado, momento em que a substituição do comando foi encontrada e executada.
Os sinalizadores restantes são alguns truques simples de formatação especial: ordene os elementos da matriz em ordem lexical normal (caractere) com ( o
), ordene na ordem inversa com ( O
), faça o mesmo independentemente de maiúsculas e minúsculas com ( oi
) ou ( Oi
) respectivamente, expanda o prompt %
- escapa com ( %
) (fácil de lembrar), expande escapes de barra invertida como print faz com p, força todos os caracteres para letras maiúsculas com ( U
) ou minúsculas com ( L
), coloca o primeiro caractere da string em maiúscula ou cada elemento da matriz com ( C
), aparece caracteres especiais como sequências de escape com ( V
). Isso deve ser suficiente para continuar.