Posix exige printf
« s %-20s
compter les 20 en termes d' octets non des caractères , même si cela fait peu de sens comme printf
est d'imprimer du texte , au format (voir la discussion au sein du groupe Austin (POSIX) et les bash
listes de diffusion).
La printf
construction de bash
et la plupart des autres obus POSIX rendent hommage à cela.
zsh
ignore cette exigence stupide (même en sh
émulation) et printf
fonctionne donc comme prévu. Idem pour les fonctions printf
intégrées de fish
(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.
$ printf %s 'Früchte und Gemüse' | wc -mcL
18 20 18
Cette chaîne est composée de 18 caractères et a une largeur de 18 colonnes ( -L
étant une wc
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
ou fish
, 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
:
$ printf '%3s|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
Dans bash
:
$ printf '%3s|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
ksh93
a une %Ls
spécification de format pour compter la largeur en termes de largeur d' affichage .
$ printf '%3Ls|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
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 la roff
sortie où X
(gras X
) est écrit X\bX
), bien que ksh93
tous les caractères de contrôle aient une largeur de -1
.
Comme autres options, vous pouvez essayer:
printf '%s\t|\n' u ü $'u\u308' $'\u1100' | expand -t3
Cela fonctionne avec certaines expand
implémentations (pas avec GNU cependant).
Sur les systèmes GNU, vous pouvez utiliser GNU awk
dont le printf
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):
gawk 'BEGIN {for (i = 1; i < ARGC; i++) printf "%-3s|\n", ARGV[i]}
' u ü $'u\u308' $'\u1100'
Si la sortie est dirigée vers un terminal, vous pouvez également utiliser des séquences d'échappement de positionnement du curseur. Comme:
forward21=$(tput cuf 21)
printf '%s\r%s%s\n' \
"Früchte und Gemüse" "$forward21" "foo" \
"Milchprodukte" "$forward21" "bar" \
"12345678901234567890" "$forward21" "baz"