Centralizar texto com printf

Centralizar texto com printf
printf "%*s\n" $(((${#fname}+$COLUMNS)/2)) "$fname"

Eu recebo este erro:

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

Isso funciona no terminal, mas não no meu script. Você tem alguma ideia?

Responder1

Nem todos os shells definem a $COLUMNSvariável para a largura do terminal.

bashversões anteriores a 5.0 apenas o configuram quando interativos, não em scripts. No entanto, desde a versão 4.3, você ainda pode habilitá-lo em shells não interativos com shopt -s checkwinsize.

Em ambos os casos, há, no entanto, uma diferença: com essa opção habilitada em shells não interativos (habilitados por padrão desde 5.0), $COLUMNS/ $LINESnão são definidos até que um processo filho seja esperado e encerrado (a NEWSentrada na fonte mencionaapós a saída de um trabalho em primeiro plano, o que é um pouco enganador, visto que não há controle de trabalho por padrão em shells não interativos). Portanto, você precisa ter certeza de que um comando externo ou subshell foi executado de forma síncrona antes de usar essas variáveis:

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

Observe também que isso só acontece se stderr for para o terminal (e se não, $COLUMNSpermanecer não definido), então você pode querer usar algo como ${COLUMNS:-80}usar um padrão mais sensato quando bashnão for possível determinar a largura da tela.

Alternativamente, você pode mudar para zsho qual sempre define $COLUMNSmesmo quando não interativo, desde que esteja sendo executado em um terminal (e $COLUMNSo padrão é 80 caso contrário) ou, em qualquer shell do tipo Bourne, use ${COLUMNS:=$(tput cols)}no lugar de $COLUMNSfor para $COLUMNSser definido a partir da saída de tput colsif ele estava previamente desdefinido ou vazio.

Se tput colsnão funcionar no seu sistema, você pode tentar </dev/tty stty size | awk '{print $2}'ouzsh -c 'print $COLUMNS'

No entanto, tenha cuidado, pois uma vez $COLUMNSdefinido dessa forma, ele não será atualizado sempre que o terminal for redimensionado¹; portanto, você pode querer usar $(tput cols)sempre para que o tamanho do terminal seja consultado toda vez que você imprimir texto centralizado em seu script.

Também tome cuidado com issoprintf '%*s'em shells diferentes de zshe fishpreenche o texto para o número determinado debytesnãopersonagens, de modo que essa abordagem só pode ser usada para preencher texto contendo caracteres de byte único e largura única que em localidades que usam UTF-8 são limitadas aos US-ASCII (0,011% de todos os caracteres possíveis).

Se estiver usando zshem vez de bash, você poderá usar os sinalizadores de expansão dos parâmetros de preenchimento left e right (que podem até lidar com caracteres de largura zero ou largura dupla com o msinalizador):

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

Observe que ele se desloca tanto para a esquerda quanto para a direita (portanto, para a borda direita da tela). Você pode remover o preenchimento correto (junto com todos os espaços em branco à direita) com:

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

Centralizar o texto que contém cores/negrito/destaque... sequências de escape seria mais complicado. Provavelmente, o mais fácil seria removê-los antes de obter a largura da string. Por exemplo, com zsh, usandoessa abordagempara determinar a largura da string (e lidar com caracteres de largura 0 ou largura dupla).

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}

¹ embora na maioria dos sistemas você possa instalar um manipulador para o sinal SIGWINCH como @zevzek mostrou nos comentários, o que ajudaria nos casos mais comuns.

Responder2

Em vez da variável COLUMNS você pode tentar obter o valor de uma fonte externa:

tput cols 

stty size | cut '-d ' -f1

Responder3

Experimente o seguinte:

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

COLUMNS não é definido automaticamente no script, então uma boa opção é usar stty para obter o tamanho atual da janela. isso funcionará em vários shells (incluindo bash, ksh, zsh)

informação relacionada