10

How can I print $myvar padded so that it is in the center of the terminal, and to either side are = to the edge of the screen?

6 Answers 6

15

I found two pieces of information here on the stackexchange network that helped me arrive at this working answer:

However the code in this answer is my own.

See the edit history if you want more verbosity; I've edited out all the cruft and "steps along the way."


I think the best way is:

center() {
  termwidth="$(tput cols)"
  padding="$(printf '%0.1s' ={1..500})"
  printf '%*.*s %s %*.*s\n' 0 "$(((termwidth-2-${#1})/2))" "$padding" "$1" 0 "$(((termwidth-1-${#1})/2))" "$padding"
}
center "Something I want to print"

Output on a terminal 80 columns wide:

========================== Something I want to print ===========================

Note that the padding doesn't have to be a single character; in fact the padding variable isn't, it's 500 characters long in the above code. You could use some other form of padding by changing just the padding line:

padding="$(printf '%0.2s' ^v{1..500})"

Results in:

^v^v^v^v^v^v^v^v^v^v^v^v^v Something I want to print ^v^v^v^v^v^v^v^v^v^v^v^v^v^

Another handy use is:

clear && center "This is my header"
2
  • can you set the padding string as a variable?
    – qodeninja
    Commented Aug 27, 2019 at 21:27
  • @qodeninja the padding string is a variable in the code shown. If you mean can it be hard coded as a literal long string rather than set using a fancy printf command, the answer is yes. :)
    – Wildcard
    Commented Aug 28, 2019 at 5:11
1

With zsh:

$ var='some text'                                                              |
$ print -r - ${(l[COLUMNS/2][=]r[COLUMNS-COLUMNS/2][=])var}                    |
===================================some text===================================|

That both left-pads and right-pads with = to half the number of columns in your terminal. With the m flag, the padding takes into account the width of characters in case there are zero-width or double-width characters in $var.

With bash and GNU wc, you can do the same with:

var='some text'
width=$(wc -L <<< "$var")
printf -v pad "%$(( (COLUMNS - width) / 2 ))s"
pad=${pad// /=}
printf '%s%.*s\n' "$pad$var$pad" "$(((COLUMNS-width)%2))" =

Where we use GNU wc -L to get the display width of $var. If your strings will only ever contain single-width characters, you can use width=${#var} instead.

Note that in any case, those assume $var doesn't contain control characters (including TAB, NL, CR, colouring escape sequences, etc).

0

This proposition seems functional but implies that the terminal supports the terminfo capabilities cols, hpa, ech, cuf, and cud1, c.f. tput (1), terminfo (5), infocmp (1m).

#!/bin/bash

# Function "center_text": center the text with a surrounding border

# first argument: text to center
# second argument: glyph which forms the border
# third argument: width of the padding

center_text()
{
    local terminal_width=$(tput cols)    # query the Terminfo database: number of columns
    local text="${1:?}"                  # text to center
    local glyph="${2:-=}"                # glyph to compose the border
    local padding="${3:-2}"              # spacing around the text

    local border=                        # shape of the border
    local text_width=${#text}

    # the border is as wide as the screen
    for ((i=0; i<terminal_width; i++))
    do
        border+="${glyph}"
    done

    printf "$border"

    # width of the text area (text and spacing)
    local area_width=$(( text_width + (padding * 2) ))

    # horizontal position of the cursor: column numbering starts at 0
    local hpc=$(( (terminal_width - area_width) / 2 ))

    tput hpa $hpc                       # move the cursor to the beginning of the area

    tput ech $area_width                # erase the border inside the area without moving the cursor
    tput cuf $padding                   # move the cursor after the spacing (create padding)

    printf "$text"                      # print the text inside the area

    tput cud1                           # move the cursor on the next line
}

center_text "Something I want to print" "~"
center_text "Something I want to print" "=" 6

The following proposition is robust and extensible and clearer than @Wildcard's solution.

#!/bin/bash

# Function "center_text": center the text with a surrounding border

# first argument: text to center
# second argument: glyph which forms the border
# third argument: width of the padding

center_text()
{

    local terminal_width=$(tput cols)     # query the Terminfo database: number of columns
    local text="${1:?}"                   # text to center
    local glyph="${2:-=}"                 # glyph to compose the border
    local padding="${3:-2}"               # spacing around the text

    local text_width=${#text}             

    local border_width=$(( (terminal_width - (padding * 2) - text_width) / 2 ))

    local border=                         # shape of the border

    # create the border (left side or right side)
    for ((i=0; i<border_width; i++))
    do
        border+="${glyph}"
    done

    # a side of the border may be longer (e.g. the right border)
    if (( ( terminal_width - ( padding * 2 ) - text_width ) % 2 == 0 ))
    then
        # the left and right borders have the same width
        local left_border=$border
        local right_border=$left_border
    else
        # the right border has one more character than the left border
        # the text is aligned leftmost
        local left_border=$border
        local right_border="${border}${glyph}"
    fi

    # space between the text and borders
    local spacing=

    for ((i=0; i<$padding; i++))
    do
        spacing+=" "
    done

    # displays the text in the center of the screen, surrounded by borders.
    printf "${left_border}${spacing}${text}${spacing}${right_border}\n"
}

center_text "Something I want to print" "~"
center_text "Something I want to print" "=" 6
0

To center text with bordering characters :

center() {
term_width=$(tput cols)
string_width=$(echo "$1" | wc -m)
# echo $term_width $string_width
padding=$(awk "BEGIN {print int(($term_width-$string_width)/2)}")
# echo $padding
len=$padding ch='='
printf '%*s' "$len" | tr ' ' "$ch"; printf "$1"; printf '%*s' "$len" | tr ' ' "$ch"; echo;
}

center " Something I want to print "

Output :

========================== Something I want to print ==========================

To print centered text with bordering lines :

center() {
printf '=%.0s' $(seq 1 $(tput cols))
echo "$1" | sed  -e :a -e "s/^.\{1,$(tput cols)\}$/ & /;ta" | tr -d '\n' | head -c $(tput cols)
printf '=%.0s' $(seq 1 $(tput cols)) | sed 's/^ //'
}

center " Something I want to print "

Output :

================================================================================
                            Something I want to print                           
================================================================================
3
  • wc -c counts the number of bytes, not the number of characters (for which you need wc -m) nor display width (for which some implementations have wc -L). Commented Sep 19, 2021 at 14:15
  • I tested both the scripts on Mac and Ubuntu Linux. They work pretty well and meets the requirement.... Commented Sep 19, 2021 at 15:04
  • Thanks Stéphane Chazelas for the correction.... Commented Sep 19, 2021 at 15:18
0

You can just do

WIDTH=$(tput cols)
WIDTHDIVIDED=$(($WIDTH/2)) # Solves "tput cols divided by 2"
clear
tput cup 0 $WIDTHDIVIDED # Puts the start of the PS1 at column 0 and the terminal width divided by two

Hope that helps.

1
  • 1
    Imagine the output string is 30 characters long on an 80 character screen. The first character of the string will be at position 40. To be centred it would need to start at character position 25. Also doesn't have the "= either side to the edge of the screen" requirement Commented Jan 26, 2023 at 16:28
0

Very similar to other answers, but I believe this is a POSIX solution as it prefers $COLUMNS over the non-standard tput cols:

text='unicode áåà string'

offset=$( echo "$text" | awk -vcols=${COLUMNS:-$(tput cols)} '{ start = (cols - length($0))/2; print start < 0 ? 0 : start }')
padding=$(printf "%*s" $offset | tr ' ' '=')
printf "%s%s%s\n" "$padding" "$text" "$padding"

Output

===============================unicode áåà string===============================

Strings with an odd length will leave a single space at the rightmost side of the terminal output. Strings with a length exceeding the width of the terminal will output with no padding.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .