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 $COLUMNS
variável para a largura do terminal.
bash
versõ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
/ $LINES
não são definidos até que um processo filho seja esperado e encerrado (a NEWS
entrada 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, $COLUMNS
permanecer não definido), então você pode querer usar algo como ${COLUMNS:-80}
usar um padrão mais sensato quando bash
não for possível determinar a largura da tela.
Alternativamente, você pode mudar para zsh
o qual sempre define $COLUMNS
mesmo quando não interativo, desde que esteja sendo executado em um terminal (e $COLUMNS
o padrão é 80 caso contrário) ou, em qualquer shell do tipo Bourne, use ${COLUMNS:=$(tput cols)}
no lugar de $COLUMNS
for para $COLUMNS
ser definido a partir da saída de tput cols
if ele estava previamente desdefinido ou vazio.
Se tput cols
nã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 $COLUMNS
definido 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 zsh
e fish
preenche 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 zsh
em vez de bash
, você poderá usar os sinalizadores de expansão dos parâmetros de preenchimento l
eft e r
ight (que podem até lidar com caracteres de largura zero ou largura dupla com o m
sinalizador):
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)