Что было бы наиболее близко к переносимому способу получения ширины дисплея (по крайней мере, на терминале (тот, который отображает символы в текущей локали с правильной шириной)) строки символов из сценария оболочки.
В первую очередь меня интересует ширина неконтролирующих символов, но приветствуются также решения, учитывающие управляющие символы, такие как возврат, возврат каретки, горизонтальная табуляция.
Другими словами, я ищу для оболочки API вокруг wcswidth()
функции POSIX.
Эта команда должна вернуть:
$ that-command 'unix' # 4 fullwidth characters
8
$ that-command 'Stéphane' # 9 characters, one of which zero-width
8
$ that-command 'もで 諤奯ゞ' # 5 double-width Japanese characters and a space
11
Можно использовать ksh93
s, printf '%<n>Ls'
который учитывает ширину символов для заполнения <n>
столбцов, или col
команду (например, с printf '++%s\b\b--\n' <character> | col -b
), чтобы попытаться получить это, perl
по крайней мере, есть модуль Text :: CharWidth , но есть более прямые или переносимые подходы.
Это более или менее продолжение этого другого вопроса, касающегося отображения текста в правой части экрана, для которого вам потребуется эта информация перед отображением текста.
источник
Ответы:
В эмуляторе терминала можно использовать отчет о положении курсора, чтобы получить позиции до / после, например, из
и узнайте, насколько широко символы напечатаны на терминале. Поскольку это управляющая последовательность ECMA-48 (а также VT100), поддерживаемая практически любым терминалом, который вы, вероятно, будете использовать, она довольно переносима.
Для справки
В конечном счете, эмулятор терминала определяет ширину печати из-за следующих факторов:
wcswidth
один не говорит о том, как обрабатываются комбинированные символы; POSIX не упоминает этот аспект в описании этой функции.wcswidth
один (см., например, главу 2. Настройка Cygwin ).xterm
например, есть положение для выбора символов двойной ширины для конфигураций, необходимых для этого.Вызов API оболочки
wcswidth
поддерживается в различной степени:Они более или менее прямые: симуляция
wcswidth
в случае Perl, вызов C времени выполнения из Ruby и Python. Вы даже можете использовать проклятия, например, из Python (которые будут обрабатывать комбинированные символы):filter
функцию (для отдельных строк)addstr
, проверяя на наличие ошибок (если она слишком длинная), а затем на конечную позициюendwin
(который не должен делатьrefresh
)Использование проклятий для вывода (вместо подачи информации обратно в скрипт или прямого вызова
tput
) очистит всю строку (filter
ограничивает ее строкой).источник
wcswidth()
вообще можно сказать о чем-либо.plink
, что он устанавливается,TERM=xterm
даже если он не реагирует ни на одну последовательность управления. Но я не использую очень экзотические терминалы.fold
видимо, предназначен для работы с многобайтовыми символами и символами расширенной ширины . Вот как он должен обрабатывать возврат: текущий счетчик ширины линии должен быть уменьшен на единицу, хотя счетчик никогда не станет отрицательным. Утилита fold не должна вставлять <newline> непосредственно перед или после любого <backspace>, если только следующий символ не имеет ширину больше 1 и не приведет к тому, что ширина строки превысит ширину. может бытьfold -w[num]
иpr +[num]
можно было как-то объединиться?Для однострочных строк в реализации GNU
wc
есть опция-L
(aka--max-line-length
), которая делает именно то, что вы ищете (кроме символов управления).источник
tab
также (предполагает табуляцию каждые 8 столбцов).wc -L <<< 'unix'
→ 8,wc -L <<< 'Stéphane'
→ 8 иwc -L <<< 'もで 諤奯ゞ'
→ 11. PS. Вы считаете, что «Стефан» - это девять символов, один из которых имеет нулевую ширину? Это выглядит как восемь символов, один из которых многобайтовый.По моему
.profile
, я вызываю скрипт для определения ширины строки в терминале. Я использую это при входе в систему на консоли компьютера, на котором я не доверяю системному наборуLC_CTYPE
, или когда я вхожу удаленно и не могу доверять,LC_CTYPE
чтобы соответствовать удаленной стороне. Мой сценарий запрашивает терминал, а не вызывает какую-либо библиотеку, потому что в этом был смысл всего моего использования: определить кодировку терминала.Это хрупко по нескольким причинам:
plink
метода получает доступ к удаленным файлам с компьютера Linux , и я решил эту проблему, используяplinkx
метод .)Это может или не может соответствовать вашему варианту использования.
Сценарий возвращает ширину в своем состоянии возврата, обрезанную до 100. Пример использования:
источник
printf "\r%*s\r" $((${#text}+8)) " ";
в конецcleanup
(добавление 8 произвольно; оно должно быть достаточно длинным, чтобы охватить более широкий вывод старых локалей, но достаточно узким, чтобы избежать переноса строк). Это делает тест невидимым, хотя и предполагает, что в строке ничего не напечатано (что хорошо в a~/.profile
)text="Éé"
а затем${#text}
дать вам ширину дисплея (я получаю4
в терминале, не2
поддерживающем юникод, и в терминале, совместимом с юникодом). Это не верно для Баш.${#text}
не дает вам ширину экрана. Он дает вам количество символов в кодировке, используемой текущей локалью. Что бесполезно для моей цели, так как я хочу определить кодировку терминала. Это полезно, если вам нужна ширина экрана по какой-то другой причине, но она не точна, потому что не каждый символ имеет ширину в одну единицу. Например, объединяющие акценты имеют ширину 0, а китайские идеограммы имеют ширину 2.Эрик Пруитт написал впечатляющую реализацию
wcwidth()
иwcswidth()
на Awk, доступную на wcwidth.awk . В основном это обеспечивает 4 функциигде
wcscolumns()
также допускает непечатные символы.Я открыл вопрос об обработке TAB, поскольку их
wcscolumns($'My sign is\t鼠鼠')
должно быть больше 14. Обновление: Эрик добавил функциюwcsexpand()
для расширения TAB до пробелов:источник
Чтобы расширить намеки на возможные решения, используя
col
иksh93
в моем вопросе:Использование
col
frombsdmainutils
в Debian (может не работать с другимиcol
реализациями), чтобы получить ширину одного неуправляемого символа:Пример:
Расширен для строки:
Используя
ksh93
'sprintf '%Ls'
:Используя
perl
'sText::CharWidth
:источник