How do I find the width & height of a terminal window?

Viewed 209515

As a simple example, I want to write a CLI script which can print = across the entire width of the terminal window.

#!/usr/bin/env php
<?php
echo str_repeat('=', ???);

or

#!/usr/bin/env python
print '=' * ???

or

#!/usr/bin/env bash
x=0
while [ $x -lt ??? ]; do echo -n '='; let x=$x+1 done; echo
10 Answers
  • tput cols tells you the number of columns.
  • tput lines tells you the number of rows.

In bash, the $LINES and $COLUMNS environmental variables should be able to do the trick. The will be set automatically upon any change in the terminal size. (i.e. the SIGWINCH signal)

yes = | head -n$(($(tput lines) * $COLUMNS)) | tr -d '\n'

On POSIX, ultimately you want to be invoking the TIOCGWINSZ (Get WINdow SiZe) ioctl() call. Most languages ought to have some sort of wrapper for that. E.g in Perl you can use Term::Size:

use Term::Size qw( chars );

my ( $columns, $rows ) = chars \*STDOUT;

Getting the window width

This shell code makes a global variable $TERM_SIZE track the size of the terminal window:

set_term_size() {
    TERM_SIZE="$(stty size 2>/dev/null)" && [ "$TERM_SIZE" ] ||
        TERM_SIZE='25 80'
}
trap set_term_size WINCH
set_term_size

It tries stty size before falling back to assuming that the terminal is 25 lines high and 80 characters wide. POSIX does not mandate the size operand for stty`, so the fallback is needed.

You can then access the columsn argument by using the shell's limited string substitution capabilities:

echo "${TERM_SIZE% *}" # Prints the terminal's height.
echo "${TERM_SIZE#* }" # Prints the terminal's width.

Of course, the scripting language you use likely offers a library that takes care of that for you -- and you should use it.

Printing a line

Once you know the width of the terminal, printing a horizontal line is easy, for example, by abusing printf's string padding:

printf '%*s\n' "${TERM_SIZE#* }" '' | 
tr ' ' -

The first line tells printf to print as many spaces as there are columns (by abusing string paddin) to a pipe. Note, POSIX does not mention the * syntax, so this may not be as portable as the code above.

The second line tells tr to read from that pipe and replace every space with a hypen.

There are some cases where your rows/LINES and columns do not match the actual size of the "terminal" being used. Perhaps you may not have a "tput" or "stty" available.

Here is a bash function you can use to visually check the size. This will work up to 140 columns x 80 rows. You can adjust the maximums as needed.

function term_size
{
    local i=0 digits='' tens_fmt='' tens_args=()
    for i in {80..8}
    do
        echo $i $(( i - 2 ))
    done
    echo "If columns below wrap, LINES is first number in highest line above,"
    echo "If truncated, LINES is second number."
    for i in {1..14}
    do
        digits="${digits}1234567890"
        tens_fmt="${tens_fmt}%10d"
        tens_args=("${tens_args[@]}" $i)
    done
    printf "$tens_fmt\n" "${tens_args[@]}"
    echo "$digits"
}
Related