Si j'exécute le script simple suivant:
#!/bin/bash
printf "%-20s %s\n" "Früchte und Gemüse" "foo"
printf "%-20s %s\n" "Milchprodukte" "bar"
printf "%-20s %s\n" "12345678901234567890" "baz"
Il imprime:
Früchte und Gemüse foo
Milchprodukte bar
12345678901234567890 baz
c'est-à-dire que le texte avec des trémas (tels que ü
) est "réduit" d'un caractère par tréma.
Certes, je me suis trompé de réglage quelque part, mais je ne suis pas en mesure de déterminer lequel pourrait être.
Cela se produit si le codage du fichier est UTF-8.
Si je modifie son codage en latin-1, l'alignement est correct, mais les trémas sont erronés:
Fr�chte und Gem�se foo
Milchprodukte bar
12345678901234567890 baz
echo Früchte und Gemüse | wc -c -m
pour la différence.printf
est.Réponses:
Posix exige
printf
« s%-20s
compter les 20 en termes d' octets non des caractères , même si cela fait peu de sens commeprintf
est d'imprimer du texte , au format (voir la discussion au sein du groupe Austin (POSIX) et lesbash
listes de diffusion).La
printf
construction debash
et la plupart des autres obus POSIX rendent hommage à cela.zsh
ignore cette exigence stupide (même ensh
émulation) etprintf
fonctionne donc comme prévu. Idem pour les fonctionsprintf
intégrées defish
(pas un shell semblable à POSIX).Le
ü
caractère (U + 00FC), lorsqu'il est codé en UTF-8, est composé de deux octets (0xc3 et 0xbc), ce qui explique la différence.Cette chaîne est composée de 18 caractères et a une largeur de 18 colonnes (
-L
étant unewc
extension GNU pour indiquer la largeur d'affichage de la ligne la plus large de l'entrée), mais elle est codée sur 20 octets.Dans
zsh
oufish
, le texte serait aligné correctement.Maintenant, il y a aussi des caractères qui ont une largeur nulle (comme des caractères combinés tels que U + 0308, la diarésie combinante) ou qui ont une double largeur, comme dans de nombreux scripts asiatiques (sans parler des caractères de contrôle comme Tab) et
zsh
qui ne s'alignaient même pas. ceux correctement.Exemple, dans
zsh
:Dans
bash
:ksh93
a une%Ls
spécification de format pour compter la largeur en termes de largeur d' affichage .Cela ne fonctionne toujours pas si le texte contient des caractères de contrôle tels que TAB (comment pourrait-il? Il
printf
faudrait savoir à quelle distance se trouvent les taquets de tabulation dans le périphérique de sortie et à quelle position il commence à imprimer). Cela fonctionne par accident avec les caractères de retour arrière (comme dans laroff
sortie oùX
(grasX
) est écritX\bX
), bien queksh93
tous les caractères de contrôle aient une largeur de-1
.Comme autres options, vous pouvez essayer:
Cela fonctionne avec certaines
expand
implémentations (pas avec GNU cependant).Sur les systèmes GNU, vous pouvez utiliser GNU
awk
dont leprintf
nombre de caractères (pas d'octets, ni de largeur d'affichage, donc toujours pas OK pour les caractères de largeur 0 ou 2, mais OK pour votre exemple):Si la sortie est dirigée vers un terminal, vous pouvez également utiliser des séquences d'échappement de positionnement du curseur. Comme:
la source
ü
caractère peut être composé deu
+¨
, ce qui correspond à 3 octets. Dans le cas de la question, il est codé en 2 caractères, mais tous neü
sont pas créés de manière égale.u\u308
est deux caractères (wc -m
au moins dans Unix / sens) pour un glyphe / graphem / graphem-cluster et est déjà mentionné et inclus dans cette réponse.printf(3)
(peu de sens après l'exigence C99 que vous mentionnez, merci pour cela), mais pas à l'printf(1)
utilitaire car chaque opérateur de shell ou autre utilitaire texte traite des caractères (ou a été modifié pour traiter également des caractères commewc
qui a un-m
(en-c
restant octet ) oucut
qui a un-b
après-c
peut signifier autre chose que des octets).En fait, non, mais votre terminal ne parle pas latin-1, et vous obtenez donc de la malbouffe plutôt que des umlauts.
Vous pouvez résoudre ce problème en utilisant iconv:
(ou exécutez tout le script shell dans iconv)
la source