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 ширины, но в порядке для вашего образца):
gawk 'BEGIN {for (i = 1; i < ARGC; i++) printf "%-3s|\n", ARGV[i]}
' u ü $'u\u308' $'\u1100'
Если вывод идет на терминал, вы также можете использовать escape-последовательности позиционирования курсора. Подобно:
forward21=$(tput cuf 21)
printf '%s\r%s%s\n' \
"Früchte und Gemüse" "$forward21" "foo" \
"Milchprodukte" "$forward21" "bar" \
"12345678901234567890" "$forward21" "baz"