Estoy trabajando en un mensaje ZSH personalizado y quiero repetir un carácter n
en una cadena (como espacios para relleno). Esta cadena está impresa con print -rP
(la -r
bandera ignora las convenciones de escape de eco y la -P
bandera realiza expansiones rápidas).
Tengo un código de trabajo que utiliza algún tipo de sustitución de cadenas, pero no sé cómo funciona. Por alguna razón, tengo que multiplicar el número de caracteres que quiero imprimir por dos, lo que parece un truco.
$ 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
Entonces, 1) ¿por qué funciona esto cuando se multiplica por dos y 2) cuál es la sintaxis correcta para repetir un carácter dentro de una cadena?
Respuesta1
1) ¿Por qué funciona esto cuando se multiplica por dos?
La expansión "${(l:3::$c:)}"
se expande a c$c
mientras que "${(l:3*2::$c:)}"
se expande a $c$c$c
. Si se establece la opción PROMPT_SUBST
y esta cadena se utiliza como parte de una cadena de solicitud, se evalúa la expansión de parámetros, la sustitución de comandos y la expansión aritmética. Entonces si c=a
, entonces c$c
se vuelve ca
y $c$c$c
se vuelve aaa
.
Prueba con 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
y 2) ¿cuál es la sintaxis correcta para repetir un carácter dentro de una cadena?
El l
indicador de expansión de parámetros se puede usar de la misma manera que ya lo está usando. Sin embargo, la p
bandera debe usarse para permitir $c
que el argumento de cadena se tome como el valor de la variable c
antes del relleno (gracias @StéphaneChazelas por señalar esto).
$ n=3 c=a zsh -xc 'print -r -- "${(pl:n::$c:)}"'
+zsh:1> print -r -- aaa
aaa
Tenga en cuenta que esta es la única forma de expansión de parámetros aceptada por esta construcción, según man zshexpn
(en la sección sobre Indicadores de expansión de parámetros):
p
Reconozca las mismas secuencias de escape que losAlternativamente, con esta opción los argumentos de cadena pueden tener la forma
$var
en cuyo caso se sustituye el valor de la variable. Tenga en cuenta que este formulario es estricto; el argumento de cadena no sufre una expansión de parámetros general.
Respuesta2
Usando su notación original, puede lograr lo que deseaba, usando p
al mismo tiempocomienzode las banderas de parámetros
print "${(pl:$n::$c:)}"
Para obtener más información y algunos otros ejemplos útiles, consulte la sección5.4.6: Aún más indicadores de parámetrosenCapítulo 5: Sustitucionesde la guía zsh. Menciona mayúsculas P
, pero no p
:
Aquí hay algunas otras marcas de parámetros; Estoy repitiendo algunos de estos. Uno muy útil es t
decirte el tipo de parámetro. Esto también surgió en el capítulo 3. Su uso más común es probar el tipo básico del parámetro antes de intentar usarlo:
if [[ ${(t)myparam} != *assoc* ]]; then
# $myparam is not an associative array. Do something about it.
fi
Otro tipo muy útil es para el relleno izquierdo o derecho de una cadena, hasta una longitud específica y, opcionalmente, con una cadena de relleno especificada para usar en lugar de espacio; incluso puede especificar una cadena única para que vaya justo al lado de la cadena en cuestión.
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 lo bastante bonito:
XXXXXXXXYa aYXXXXXXXX
XXXXXXXYab abYXXXXXXX
XXXXXXYabc abcYXXXXXX
XXXXXYabcd abcdYXXXXX
XXXXYabcde abcdeYXXXX
XXXYabcdef abcdefYXXX
XXYabcdefg abcdefgYXX
XYabcdefgh abcdefghYX
Yabcdefghi abcdefghiY
abcdefghij abcdefghij
Tenga en cuenta que esos dos puntos (que pueden ser otros caracteres, como expliqué para las banderas ( s
) y ( j
)) siempre aparecen en pares antes y después del argumento, de modo que con tres argumentos, los dos puntos intermedios se duplican. Puedes perderte la :Y:
parte y la :X:
parte y ver qué pasa. No es necesario que las cadenas de relleno sean caracteres individuales; si no caben un número exacto de veces en el espacio de relleno, la última repetición se truncará en el extremo más alejado del argumento del parámetro que se inserta.
Dos parámetros le dicen al shell que desea que se haga algo especial con el valor de sustitución del parámetro. El P
indicador ( ) obliga a que el valor se trate como un nombre de parámetro, de modo que se obtiene el efecto de una doble sustitución:
% final=string
% intermediate=final
% print ${(P)intermediate}
string
Esto es un poco como si $intermediate
fuera lo que en ksh se llama a nameref
, un parámetro que se marca como referencia a otro parámetro. Es posible que Zsh eventualmente también los tenga; hay lugares donde son mucho más convenientes que la (P)
bandera.
Una bandera más poderosa es ( e
), que obliga a volver a escanear el valor para todas las formas de sustitución de una sola palabra. Por ejemplo,
% foo='$(print $ZSH_VERSION)'
% print ${(e)foo}
4.0.2
hizo que el valor de $foo
fuera reexaminado, momento en el cual se encontró y ejecutó la sustitución del comando.
Las banderas restantes son algunos trucos de formato especiales simples: ordenar los elementos de la matriz en orden léxico normal (caracteres) con ( o
), ordenar en orden inverso con ( O
), hacer lo mismo entre mayúsculas y minúsculas de forma independiente con ( oi
) o ( Oi
) respectivamente, expandir el mensaje %
- escapa con ( %
) (fácil de recordar), expande la barra invertida escapa como lo hace print con p, fuerza todos los caracteres a mayúsculas con ( U
) o minúsculas con ( L
), escribe en mayúscula el primer carácter de la cadena o cada elemento de la matriz con ( C
), aparece caracteres especiales como secuencias de escape con ( V
). Eso debería ser suficiente para seguir adelante.