POSIX requiere printf
Es %-20s
para contar los 20 en términos de bytes no personajes a pesar de que tiene poco sentido como printf
es imprimir texto , formateado (véase la discusión en el Grupo Austin (POSIX) y bash
listas de correo).
La printf
construcción de bash
y la mayoría de los otros proyectiles POSIX honran eso.
zsh
ignora ese requisito tonto (incluso en la sh
emulación), por lo que printf
funciona como es de esperar allí. Lo mismo para la printf
construcción de fish
(no un shell POSIX).
El ü
carácter (U + 00FC), cuando se codifica en UTF-8, está formado por dos bytes (0xc3 y 0xbc), lo que explica la discrepancia.
$ printf %s 'Früchte und Gemüse' | wc -mcL
18 20 18
Esa cadena está hecha de 18 caracteres, tiene 18 columnas de ancho ( -L
siendo una wc
extensión GNU para informar el ancho de visualización de la línea más ancha en la entrada) pero está codificada en 20 bytes.
En zsh
o fish
, el texto se alinearía correctamente.
Ahora, también hay caracteres que tienen ancho 0 (como combinar caracteres como U + 0308, la diéresis combinada) o tienen doble ancho como en muchos scripts asiáticos (sin mencionar caracteres de control como Tab) e incluso zsh
no se alinearían esos correctamente.
Ejemplo, en zsh
:
$ printf '%3s|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
En bash
:
$ printf '%3s|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
ksh93
tiene una %Ls
especificación de formato para contar el ancho en términos de ancho de pantalla .
$ printf '%3Ls|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
Eso todavía no funciona si el texto contiene caracteres de control como TAB (¿cómo podría? printf
Tendría que saber qué tan separados están los tabuladores en el dispositivo de salida y en qué posición comienza a imprimir). Funciona por accidente con caracteres de retroceso (como en la roff
salida donde X
(negrita X
) se escribe como X\bX
) aunque ksh93
considera que todos los caracteres de control tienen un ancho de -1
.
Como otras opciones, puedes probar:
printf '%s\t|\n' u ü $'u\u308' $'\u1100' | expand -t3
Eso funciona con algunas expand
implementaciones (aunque no con GNU).
En los sistemas GNU, puede usar GNU awk
cuyos printf
recuentos en caracteres (no bytes, no anchos de visualización, por lo que aún no está bien para los caracteres de 0 o 2 anchos, pero está bien para su muestra):
gawk 'BEGIN {for (i = 1; i < ARGC; i++) printf "%-3s|\n", ARGV[i]}
' u ü $'u\u308' $'\u1100'
Si la salida va a un terminal, también puede usar secuencias de escape de posicionamiento del cursor. Me gusta:
forward21=$(tput cuf 21)
printf '%s\r%s%s\n' \
"Früchte und Gemüse" "$forward21" "foo" \
"Milchprodukte" "$forward21" "bar" \
"12345678901234567890" "$forward21" "baz"