POSIX richiede printf
'il %-20s
contare quelli 20 in termini di byte e non personaggi , anche se questo ha poco senso, come printf
è quello di stampare il testo formattato (vedi la discussione presso il Gruppo di Austin (POSIX) e bash
mailing list).
L' printf
integrato bash
e la maggior parte delle altre shell POSIX lo onorano.
zsh
ignora quel requisito sciocco (anche in sh
emulazione), quindi printf
funziona come ci si aspetterebbe lì. Lo stesso per l' printf
integrato di fish
(non una shell simile a POSIX).
Il ü
carattere (U + 00FC), quando codificato in UTF-8, è composto da due byte (0xc3 e 0xbc), il che spiega la discrepanza.
$ printf %s 'Früchte und Gemüse' | wc -mcL
18 20 18
Quella stringa è composta da 18 caratteri, è larga 18 colonne ( -L
essendo wc
un'estensione GNU per riportare la larghezza di visualizzazione della linea più ampia nell'input) ma è codificata su 20 byte.
In zsh
o fish
, il testo verrebbe allineato correttamente.
Ora, ci sono anche personaggi che hanno una larghezza 0 (come combinare caratteri come U + 0308, la combinazione di diaresi) o hanno una doppia larghezza come in molti script asiatici (per non parlare dei caratteri di controllo come Tab) e addirittura zsh
non si allineano quelli correttamente.
Esempio, in zsh
:
$ printf '%3s|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
In bash
:
$ printf '%3s|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
ksh93
ha una %Ls
specifica di formato per contare la larghezza in termini di larghezza di visualizzazione .
$ printf '%3Ls|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
Ciò non funziona ancora se il testo contiene caratteri di controllo come TAB (come potrebbe? printf
Dovrebbe sapere quanto distano i punti di tabulazione nel dispositivo di output e in quale posizione inizia a stampare). Funziona per caso con i caratteri backspace (come roff
nell'output in cui è scritto X
(grassetto X
) X\bX
) sebbene ksh93
consideri che tutti i caratteri di controllo abbiano una larghezza di -1
.
Come altre opzioni, puoi provare:
printf '%s\t|\n' u ü $'u\u308' $'\u1100' | expand -t3
Funziona con alcune expand
implementazioni (non per GNU).
Sui sistemi GNU, puoi usare GNU i awk
cui printf
conteggi in caratteri (non byte, non larghezze di visualizzazione, quindi non sono ancora OK per i caratteri 0-larghezza o 2-larghezza, ma OK per il tuo esempio):
gawk 'BEGIN {for (i = 1; i < ARGC; i++) printf "%-3s|\n", ARGV[i]}
' u ü $'u\u308' $'\u1100'
Se l'output arriva a un terminale, è anche possibile utilizzare sequenze di escape di posizionamento del cursore. Piace:
forward21=$(tput cuf 21)
printf '%s\r%s%s\n' \
"Früchte und Gemüse" "$forward21" "foo" \
"Milchprodukte" "$forward21" "bar" \
"12345678901234567890" "$forward21" "baz"