POSIX вимагає printf
, %-20s
щоб лічильники рахували ці 20 в байтах, а не символи, хоча це мало сенсу, як printf
друкувати текст , відформатований (див. Обговорення в Austin Group (POSIX) та bash
списки розсилки).
printf
Вбудований в bash
і більшість інших POSIX оболонки честь цього.
zsh
ігнорує, що нерозумна вимога (навіть в sh
емуляції) printf
працює так, як ви там очікували. Те саме для printf
вбудованої fish
(не POSIX-оболонки).
ü
Символ (U + 00FC), коли кодується в UTF-8 складається з двох байтів (0xC3 і 0xbc), що пояснює розходження.
$ printf %s 'Früchte und Gemüse' | wc -mcL
18 20 18
Цей рядок складається з 18 символів, шириною 18 стовпців ( -L
є wc
розширенням GNU для повідомлення про ширину відображення найширшого рядка на вході), але кодується на 20 байтах.
В zsh
або fish
текст буде вирівняно правильно.
Тепер також є символи, які мають 0-ширину (наприклад, комбінування символів, таких як U + 0308, комбінуючий діарез) або мають подвійну ширину, як у багатьох азіатських сценаріях (не кажучи вже про контрольні символи типу Tab) і навіть zsh
не вирівнюють ці належним чином.
Приклад zsh
:
$ printf '%3s|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
В bash
:
$ printf '%3s|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
ksh93
має %Ls
специфікацію формату для підрахунку ширини з точки зору ширини відображення .
$ printf '%3Ls|\n' u ü $'u\u308' $'\u1100'
u|
ü|
ü|
ᄀ|
Це все ще не працює, якщо текст містить контрольні символи, такі як TAB (як він міг printf
би знати, наскільки далеко розташовані зупинки вкладки у пристрої виводу та в якому положенні він починає друкувати). Це працює випадково з символами зворотної області (як, наприклад, у roff
висновку, де X
(жирним шрифтом X
) написано як X\bX
), хоча, як і ksh93
всі керуючі символи, розглядаються як з шириною -1
.
Як інші варіанти, ви можете спробувати:
printf '%s\t|\n' u ü $'u\u308' $'\u1100' | expand -t3
Це працює з деякими expand
реалізаціями (не GNU).
У системах GNU ви можете використовувати GNU awk
, printf
кількість яких рахується в символах (не байти, не ширина дисплея, тому все одно не в порядку для символів 0-ширини або 2-ширини, але OK для вашого зразка):
gawk 'BEGIN {for (i = 1; i < ARGC; i++) printf "%-3s|\n", ARGV[i]}
' u ü $'u\u308' $'\u1100'
Якщо висновок переходить до терміналу, ви також можете використовувати послідовності введення позиціонування курсору. Подібно до:
forward21=$(tput cuf 21)
printf '%s\r%s%s\n' \
"Früchte und Gemüse" "$forward21" "foo" \
"Milchprodukte" "$forward21" "bar" \
"12345678901234567890" "$forward21" "baz"