Skip to main content
2 of 8
added 858 characters in body
Stéphane Chazelas
  • 584.6k
  • 96
  • 1.1k
  • 1.7k

Not all shells set the $COLUMNS variable to the width of the terminal.

The bash shell only sets it when interactive, not in scripts.

You could switch to zsh which always sets $COLUMNS even when non-interactive or use ${COLUMNS:=$(tput cols)} in place of $COLUMNS for $COLUMNS to be set from the output of tput cols if it was previously unset or empty.

If tput cols doesn't work on your system, you can try </dev/tty stty size | awk '{print $2}', or zsh -c 'print $COLUMNS'

Beware however that once $COLUMNS has been set in that way, it won't get updated whenever the terminal is resized, so you may want to use $(tput cols) always instead so the terminal size is queried every time you print centred text in your script.

Also beware that printf '%*s' in shells other than zsh and fish pads text to the given number of bytes not characters, so that approach can only be used to pad text containing single byte, single width characters which in locales using UTF-8 is limited to the US-ASCII ones (0.011% of all possible characters).

If using zsh instead of bash, you could use its left and right padding parameter expansion flags instead (which can even handle zero-width or double-width characters with the m flag):

print -r -- ${(ml[COLUMNS/2]r[COLUMNS-COLUMNS/2])fname}
Stéphane Chazelas
  • 584.6k
  • 96
  • 1.1k
  • 1.7k