Preenchendo strings Unicode com printf do bash

Sep 13 2020

Tentei preencher uma string Unicode com printf do bash e vi isso, enquanto

printf "%2s" a

produz o esperado "a",

uma variante Unicode

printf "%2s" ä  

produz um "ä" surpreendentemente não acolchoado. (zsh dá o resultado esperado.)

O que causa isso; e como devo preencher strings Unicode no bash?

Respostas

crackpot Sep 13 2020 at 00:02

como devo preencher strings Unicode no bash?

Isso está muito além das capacidades do bash. Se você estiver limitando "strings Unicode" a ascii ++ (sem caracteres de largura dupla, sem bidi, sem marcas de não espaçamento, sem etc.), você pode júri de algo como:

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

O caractere äé codificado com 2 bytes em UTF-8, então Printf o considera como 2 preenchido.

Podemos contar os caracteres ( -m) e bytes ( -c) de uma string. O número a dar a Printf é então [intended pad]+[bytes]-[chars]. Então eu montei 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"

No exemplo de execução abaixo, eu adicionei artificialmente novas linhas após cada saída para fins de clareza.

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

bash se comporta corretamente e o programa C

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

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

se comporta da mesma forma.

Isso ocorre porque% s se refere a uma string orientada a bytes e 'ä' em UTF-8 resulta em 2 bytes.

Pelo que pude testar, nenhum dos outros shells se comporta incorretamente.

O resultado que você espera pode ser visto com algo como:

printf '%2S\n' ä

mas isso não é suportado por nenhum dos shells que testei.