Центрировать текст с помощью printf

Центрировать текст с помощью printf
printf "%*s\n" $(((${#fname}+$COLUMNS)/2)) "$fname"

Я получаю эту ошибку:

line 9: (7+)/2: syntax error: operand expected (error token is ")/2")

Это работает в терминале, но не в моем скрипте. Есть идеи?

решение1

Не все оболочки устанавливают $COLUMNSпеременную, равную ширине терминала.

bashВерсии до 5.0 устанавливают его только в интерактивном режиме, а не в скриптах. Однако, начиная с версии 4.3, вы все еще можете включить его в неинтерактивных оболочках с помощью shopt -s checkwinsize.

В любом случае, однако, есть нюанс: при включении этой опции в неинтерактивных оболочках (включено по умолчанию с версии 5.0), $COLUMNS/ $LINESне устанавливаются до тех пор, пока не будет ожидан и завершен дочерний процесс ( NEWSзапись в исходном коде упоминаетпосле завершения приоритетного задания, что немного вводит в заблуждение, учитывая, что в неинтерактивных оболочках по умолчанию нет управления заданиями). Поэтому вам нужно убедиться, что внешняя команда или подоболочка была запущена синхронно, прежде чем использовать эти переменные:

#! /bin/bash -
shopt -s checkwinsize # for versions 4.3 and 4.4
(:) # start a synchronous subshell that runs the null command
echo "$COLUMNS $LINE"

Также обратите внимание, что это происходит только в том случае, если stderr отправляется на терминал (а если нет, $COLUMNSто остается неустановленным), поэтому вы можете использовать что-то вроде ${COLUMNS:-80}более разумного значения по умолчанию, когда bashне можете определить ширину экрана.

В качестве альтернативы можно переключиться на zshwhich always sets, $COLUMNSдаже если он неинтерактивен, пока он запущен в терминале (и $COLUMNSпо умолчанию 80 в противном случае) или в любой оболочке типа Bourne использовать ${COLUMNS:=$(tput cols)}вместо $COLUMNSfor $COLUMNSдля установки из вывода , tput colsесли он ранее был не установлен или пуст.

Если tput colsне работает на вашей системе, вы можете попробовать </dev/tty stty size | awk '{print $2}', илиzsh -c 'print $COLUMNS'

Однако следует помнить, что после $COLUMNSтакой настройки он не будет обновляться при изменении размера терминала¹, поэтому вы можете использовать $(tput cols)значение always, чтобы размер терминала запрашивался каждый раз при печати центрированного текста в вашем скрипте.

Также имейте в виду, чтоprintf '%*s'в оболочках, отличных от zshи fishдополняет текст до указанного количествабайтынетперсонажи, поэтому этот подход можно использовать только для заполнения текста, содержащего однобайтовые символы одинарной ширины, которые в локалях, использующих UTF-8, ограничены символами US-ASCII (0,011% от всех возможных символов).

При использовании zshвместо bashможно использовать флаги расширения параметров заполнения left и right (которые могут обрабатывать даже символы нулевой или двойной ширины с помощью флага m):

print -r -- ${(ml[COLUMNS/2]r[COLUMNS-COLUMNS/2])fname}

Обратите внимание, что он отступает как слева, так и справа (то есть до правого края экрана). Вы можете удалить правый отступ (вместе со всеми конечными пробелами) с помощью:

set -o extendedglob
print -r -- ${${(ml[COLUMNS/2]r[COLUMNS-COLUMNS/2])fname}%%[[:space:]]#}

Центрирование текста, содержащего цветные / жирные / выделенные... escape-последовательности, будет более сложным. Проще всего, вероятно, будет удалить их перед получением ширины строки. Например, с помощью zsh, используяэтот подходдля определения ширины строки (и обработки символов нулевой или двойной ширины).

varwidth() (( ${(P)#1} * 3 - ${#${(ml[${(P)#1} * 2])${(P)1}}} ))
functions -Ms varwidth

varwidth_without_formatting() {
  set -o localoptions -o extendedglob
  local without_formatting=${(P)1//$'\e'\[[0-9;]#m}
  (( varwidth(without_formatting) ))
}
functions -Ms varwidth_without_formatting

center() {
  local text
  for text do
    print -r -- ${(l[(COLUMNS-varwidth_without_formatting(text))/2])}$text
  done
}

center $'\e[31mred\e[1;39mbold\e[m' \
       ${(%):-%F{green}Blah%F{yellow}blah%F{magenta}blah%f}

¹ хотя в большинстве систем можно установить обработчик сигнала SIGWINCH, как показал @zevzek в комментариях, что поможет в наиболее распространенных случаях.

решение2

Вместо переменной COLUMNS можно попробовать получить значение из внешнего источника:

tput cols 

stty size | cut '-d ' -f1

решение3

Попробуйте следующее:

read WindowHeight WindowWidth<<<$(stty size)
printf "%$(((${#fname}+${WindowWidth})/2))s" "$fname"

COLUMNS не задается автоматически в скрипте, поэтому хорошим выбором будет использовать stty для получения текущего размера окна. Это будет работать в нескольких оболочках (включая bash, ksh, zsh)

Связанный контент