Relleno de cadenas unicode con printf de bash

Sep 13 2020

Intenté rellenar una cadena Unicode con printf de bash, y lo vi, mientras

printf "%2s" a

produce la "a" esperada,

una variante Unicode

printf "%2s" ä  

produce una "ä" sorprendentemente sin relleno. (zsh da el resultado esperado).

Qué causa esto; y ¿cómo se supone que debo rellenar cadenas Unicode en bash?

Respuestas

crackpot Sep 13 2020 at 00:02

¿Cómo se supone que debo rellenar cadenas Unicode en bash?

Eso está mucho más allá de las capacidades de bash. Si está limitando las "cadenas Unicode" a ascii ++ (sin caracteres de doble ancho, sin bidi, sin marcas sin espaciado, sin etc.), puede montar como jurado algo como:

% pad(){ printf '%*s%s\n' "$(($1-${#2}))" "" "$2"; }
% pad 2 €
 €
Quasímodo Sep 13 2020 at 00:06

El carácter äestá codificado con 2 bytes en UTF-8, por lo que Printf lo toma por 2 rellenos.

Wc puede contar los caracteres ( -m) y bytes ( -c) de una cadena. El número para darle a Printf es entonces [intended pad]+[bytes]-[chars]. Así que reuní este pad.shscript

#!/bin/sh
bytes=$(printf '%s' "$2" | wc -c)
chars=$(printf '%s' "$2" | wc -m)
n=$(($1+bytes-chars))
printf "%${n}s" "$2"

En la ejecución de ejemplo a continuación, he agregado artificialmente nuevas líneas después de cada salida para mayor claridad.

$ sh pad.sh 10 abcdef abcdef $ sh pad.sh 10 äéßôçÈ
    äéßôçÈ
schily Sep 12 2020 at 23:48

bash se comporta correctamente y el programa C

#include <stdio.h>
main()
{
        char foo[] = "ä";

        printf("%2s\n", foo);
}

se comporta igual.

Esto se debe a que% s se refiere a una cadena orientada a bytes y 'ä' en UTF-8 da como resultado 2 bytes.

Por lo que pude probar, ninguno de los otros proyectiles se comporta incorrectamente.

El resultado que espera se puede ver con algo como:

printf '%2S\n' ä

pero esto no es compatible con ninguno de los shells que probé.