Posix exige printf « s %-20scompter les 20 en termes d' octets non des caractères , même si cela fait peu de sens comme printfest d'imprimer du texte , au format (voir la discussion au sein du groupe Austin (POSIX) et les bashlistes de diffusion).
La printfconstruction de bashet la plupart des autres obus POSIX rendent hommage à cela.
zshignore cette exigence stupide (même en shémulation) et printffonctionne donc comme prévu. Idem pour les fonctions printfinté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 wcextension 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 zshou 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 zshqui 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|
ü|
ü|
ᄀ|
ksh93a une %Lsspé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 printffaudrait 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 roffsortie où X(gras X) est écrit X\bX), bien que ksh93tous 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 expandimplémentations (pas avec GNU cependant).
Sur les systèmes GNU, vous pouvez utiliser GNU awkdont le printfnombre 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"