Remplir les chaînes Unicode avec le printf de Bash

Sep 13 2020

J'ai essayé de remplir une chaîne Unicode avec le printf de bash, et j'ai vu cela, tout en

printf "%2s" a

donne le "a" attendu,

une variante Unicode

printf "%2s" ä  

donne un "ä" étonnamment non rembourré. (zsh donne le résultat attendu.)

Qu'est-ce qui cause cela? et comment suis-je censé remplir des chaînes Unicode dans bash?

Réponses

crackpot Sep 13 2020 at 00:02

comment suis-je censé remplir des chaînes Unicode dans bash?

C'est bien au-delà des capacités de bash. Si vous limitez les "chaînes Unicode" à ascii ++ (pas de caractères double largeur, pas de bidi, pas de marques sans espacement, pas, etc.), vous pouvez faire quelque chose comme:

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

Le caractère äest encodé avec 2 octets en UTF-8, donc Printf le prend pour 2-padded.

Wc peut compter les caractères ( -m) et octets ( -c) d'une chaîne. Le numéro à donner à Printf est alors [intended pad]+[bytes]-[chars]. J'ai donc assemblé ce pad.shscript,

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

Dans l'exemple d'exécution ci-dessous, j'ai ajouté artificiellement des nouvelles lignes après chaque sortie pour plus de clarté.

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

bash se comporte correctement et le programme C

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

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

se comporte de la même manière.

Ceci est dû au fait que% s fait référence à une chaîne orientée octet et que «ä» en UTF-8 donne 2 octets.

Autant que j'ai pu tester, aucun des autres obus ne se comporte de manière incorrecte.

Le résultat que vous attendez peut être vu avec quelque chose comme:

printf '%2S\n' ä

mais cela n'est pris en charge par aucun des shells que j'ai testés.