POSIX richiede printf 'il %-20scontare 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 bashmailing list).
L' printfintegrato bashe la maggior parte delle altre shell POSIX lo onorano.
zshignora quel requisito sciocco (anche in shemulazione), quindi printffunziona come ci si aspetterebbe lì. Lo stesso per l' printfintegrato 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 ( -Lessendo wcun'estensione GNU per riportare la larghezza di visualizzazione della linea più ampia nell'input) ma è codificata su 20 byte.
In zsho 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 zshnon 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|
ü|
ü|
ᄀ|
ksh93ha una %Lsspecifica 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? printfDovrebbe 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 roffnell'output in cui è scritto X(grassetto X) X\bX) sebbene ksh93consideri 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 expandimplementazioni (non per GNU).
Sui sistemi GNU, puoi usare GNU i awkcui printfconteggi 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"