Bash: текст по центру и цвету с заполнением по обе стороны от динамической ширины терминала с помощью printf

#bash #format #printf #padding #center

Вопрос:

Я пытаюсь отформатировать вывод с помощью printf, чтобы добиться следующего:

 ----------------------------------- HEADING -----------------------------------
-------------------------------- LONGER HEADING -------------------------------
 

Однако мои попытки привели только к:

 - HEADING -
- LONGERHEADING -
 

Мой текущий код выглядит так:

 MAGENTA=$(tput setaf 5)
NOCOL=$(tput sgr0)

heading="${1^^}"
width="$(tput cols)"
padlimit=30
if [[ $width -gt 240 ]]; then
    padlimit="$(( $width/8 ))"
fi
padding="$(printf '%0.1s' -{1..$padlimit})"

printf '%s%*.*s %s %*.*s%sn' "$MAGENTA" 0 "$(((width-2-${#heading})/2))" "$padding" 
 "$heading" 0 "$(((width-2-${#heading})/2))" "$padding" "$NOCOL"
 

Если я попытаюсь жестко padding закодировать элемент счетчика переменной, например так:

 MAGENTA=$(tput setaf 5)
NOCOL=$(tput sgr0)

heading="${1^^}"
width="$(tput cols)"
padding="$(printf '%0.1s' -{1..60})"

printf '%s%*.*s %s %*.*s%sn' "$MAGENTA" 0 "$(((width-2-${#heading})/2))" "$padding" 
"$heading" 0 "$(((width-2-${#heading})/2))" "$padding" "$NOCOL"
 

Результаты таковы:

 ------------------------------ HEADING ------------------------------
------------------------------ LONGER HEADING ------------------------------
 

Я полагаю, что математика в этом printf утверждении неверна. Однако я не уверен в том, что:

  1. Какова printf должна быть арифметика в заявлении; и
  2. Почему при предоставлении padding переменной элементу count переменной она не вычисляется?

Любая помощь будет оценена по достоинству!

Комментарии:

1. Вопросительный взгляд на ваш сценарий, это приведет к сбою printf '%0.1s' -{1..$padlimit} , потому что расширение переменной происходит до того, как расширение фигурных скобок, по bash крайней мере, вставит ваш сценарий в shellcheck.net для подтверждения/рекомендации.

2. Не существует простого способа повторить символ или строку определенное количество раз с printf помощью команды или функции C. Это затрудняет создание длинной очереди. В Perl есть x оператор: perl -e 'print "-" x 32, "n"' печатает 32 тире. Другие языки сценариев имеют аналогичный синтаксис. AFAIK, printf команда не поддерживает никаких аналогичных/эквивалентных обозначений.

3. Ваши выходные данные показывают разную длину строк, поэтому вы, вероятно, не учитываете длину ваших строк. т. е. width/2 - length(header) / 2 - 1 длину префиксных и постфиксных тире. Затем вы печатаете пробел и заголовок между ними.

4. То, что вы пытаетесь сделать, бесполезно, потому что в настоящее время терминалы не имеют фиксированной ширины. Всякий раз, когда вы меняете размер окна терминала, макет, который вы пытаетесь создать, портится. Ансибль похож на глупость, и всякий раз, когда я его использую, он меня раздражает.

Ответ №1:

Просто напишите цикл for и распечатайте - в цикле for.

 center() {
     local heading width headinglen padlength i
     heading="$*"
     width="$(tput cols)"
     headinglen=${#heading}
     padlength=$(( (width - 2 - headinglen) / 2 ))
     for ((i = 0; i < padlength;   i)); do
        printf "-"
     done
     printf " %s " "$heading"
     for ((i = 0; i < padlength;   i)); do
          printf "-"
     done
     if (( (width - 2 - headinglen) % 2 )); then
          printf "-"
     fi
     printf "n"
}
center HEADING
center LONGER HEADING
 

Ответ №2:

Как указывалось в комментариях Jetchisel и Джонатана Леффлера, проблемы с вашим исходным кодом заключались в том, что расширение скобок не будет работать с переменной внутри и, следовательно printf , само по себе не может делать то, что вы хотите.

Чистым решением bash было бы использование цикла, и это устранило бы необходимость в полях ширины в команде printf:

 MAGENTA=$(tput setaf 5)
NOCOL=$(tput sgr0)

heading="${1^^}"

width="$(tput cols)"
padlength=$(((width-2-${#heading})/2))
padding=""
i=0
while [[ $i -lt $padlength ]]; do
  padding ='-'
  ((i  ))
done

printf '%s%s %s %s%sn' "$MAGENTA" "$padding" "$heading" "$padding" "$NOCOL"
 

Ограничение pad в вашем исходном коде часто нарушало единообразие длин строк в моих (небольших) окнах эмулятора терминала, но вот как вы бы реализовали его с помощью цикла:

 MAGENTA=$(tput setaf 5)
NOCOL=$(tput sgr0)

heading="${1^^}"

width="$(tput cols)"
padlimit=30
if [[ $width -gt 240 ]]; then
  padlimit="$(( $width/8 ))"
fi
padlength=$(((width-2-${#heading})/2))
padding=""
i=0
while [[ $i -lt $padlength amp;amp; $i -lt $padlimit ]]; do
  padding ='-'
  ((i  ))
done

printf '%s%s %s %s%sn' "$MAGENTA" "$padding" "$heading" "$padding" "$NOCOL"