Centrar texto con printf

Centrar texto con printf
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 $COLUMNSvariable al ancho del terminal.

bashLas 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/ $LINESno se configuran hasta que se haya esperado y salido de un proceso hijo (la NEWSentrada 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, $COLUMNSpermanece sin configurar), por lo que es posible que desee usar algo como ${COLUMNS:-80}usar un valor predeterminado más sensato cuando bashno pueda determinar el ancho de la pantalla.

Alternativamente, puede cambiar a zshque siempre se establece $COLUMNSincluso cuando no es interactivo, siempre y cuando se esté ejecutando en una terminal (y $COLUMNSde lo contrario el valor predeterminado es 80) o, en cualquier shell tipo Bourne, use ${COLUMNS:=$(tput cols)}en lugar de $COLUMNSfor para $COLUMNSque se configure desde la salida de tput colsif anteriormente estaba desarmado o vacío.

Si tput colsno 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 $COLUMNSconfigurado 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 zshy fishrellena 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 zshen lugar de bash, puede usar sus indicadores de expansión de parámetros de relleno left y rderecho (que incluso pueden manejar caracteres de ancho cero o doble con el mindicador):

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)

información relacionada