printf "%*s\n" $(((${#fname}+$COLUMNS)/2)) "$fname"
Recibo este error:
line 9: (7+)/2: syntax error: operand expected (error token is ")/2")
Esto funciona en la terminal pero no en mi script. ¿Tienes alguna idea?
Respuesta1
No todos los shells establecen la $COLUMNS
variable al ancho del terminal.
bash
Las versiones anteriores a la 5.0 solo lo configuran cuando es interactivo, no en scripts. Sin embargo, desde la versión 4.3, aún puedes habilitarlo en shells no interactivos con shopt -s checkwinsize
.
En cualquier caso, sin embargo, hay un giro: con esa opción habilitada en shells no interactivos (habilitada de forma predeterminada desde 5.0), $COLUMNS
/ $LINES
no se configuran hasta que se haya esperado y salido de un proceso hijo (la NEWS
entrada en la fuente mencionadespués de que sale un trabajo en primer plano, lo cual es un poco engañoso dado que no hay control de trabajo de forma predeterminada en shells no interactivos). Por lo tanto, debe asegurarse de que un comando externo o subshell se haya ejecutado sincrónicamente antes de usar esas variables:
#! /bin/bash -
shopt -s checkwinsize # for versions 4.3 and 4.4
(:) # start a synchronous subshell that runs the null command
echo "$COLUMNS $LINE"
También tenga en cuenta que solo sucede si stderr va a la terminal (y si no, $COLUMNS
permanece sin configurar), por lo que es posible que desee usar algo como ${COLUMNS:-80}
usar un valor predeterminado más sensato cuando bash
no pueda determinar el ancho de la pantalla.
Alternativamente, puede cambiar a zsh
que siempre se establece $COLUMNS
incluso cuando no es interactivo, siempre y cuando se esté ejecutando en una terminal (y $COLUMNS
de lo contrario el valor predeterminado es 80) o, en cualquier shell tipo Bourne, use ${COLUMNS:=$(tput cols)}
en lugar de $COLUMNS
for para $COLUMNS
que se configure desde la salida de tput cols
if anteriormente estaba desarmado o vacío.
Si tput cols
no funciona en su sistema, puede intentarlo </dev/tty stty size | awk '{print $2}'
ozsh -c 'print $COLUMNS'
Sin embargo, tenga en cuenta que una vez $COLUMNS
configurado de esa manera, no se actualizará cada vez que se cambie el tamaño del terminal¹, por lo que es posible que desee utilizar $(tput cols)
siempre para que se consulte el tamaño del terminal cada vez que imprima texto centrado en su secuencia de comandos.
También ten cuidado con esoprintf '%*s'
en shells distintos de zsh
y fish
rellena el texto con el número dado debytesnocaracteres, por lo que ese enfoque solo se puede utilizar para rellenar texto que contenga caracteres de un solo byte y ancho único que en las configuraciones regionales que usan UTF-8 se limita a los de EE. UU.-ASCII (0,011% de todos los caracteres posibles).
Si usa zsh
en lugar de bash
, puede usar sus indicadores de expansión de parámetros de relleno l
eft y r
derecho (que incluso pueden manejar caracteres de ancho cero o doble con el m
indicador):
print -r -- ${(ml[COLUMNS/2]r[COLUMNS-COLUMNS/2])fname}
Tenga en cuenta que se desplaza tanto hacia la izquierda como hacia la derecha (es decir, hacia el borde derecho de la pantalla). Puede eliminar el relleno derecho (junto con todos los espacios en blanco finales) con:
set -o extendedglob
print -r -- ${${(ml[COLUMNS/2]r[COLUMNS-COLUMNS/2])fname}%%[[:space:]]#}
Centrar texto que contenga secuencias de color, negrita, destacado... sería más complicado. Probablemente lo más fácil sería quitarlos antes de medir el ancho de la cuerda. Por ejemplo, con zsh
, usandoese enfoquepara determinar el ancho de la cadena (y manejar caracteres de ancho 0 o doble).
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}
¹ aunque en la mayoría de los sistemas se puede instalar un controlador para la señal SIGWINCH como mostró @zevzek en los comentarios, lo que ayudaría en los casos más comunes.
Respuesta2
En lugar de la variable COLUMNAS, puedes intentar obtener el valor de una fuente externa:
tput cols
stty size | cut '-d ' -f1
Respuesta3
Pruebe lo siguiente:
read WindowHeight WindowWidth<<<$(stty size)
printf "%$(((${#fname}+${WindowWidth})/2))s" "$fname"
COLUMNAS no se configura automáticamente en el script, por lo que una buena opción es usar stty para obtener el tamaño actual de la ventana. esto funcionará en múltiples shells (incluidos bash, ksh, zsh)