[python] How to get Linux console window width in Python

Is there a way in python to programmatically determine the width of the console? I mean the number of characters that fits in one line without wrapping, not the pixel width of the window.

Edit

Looking for a solution that works on Linux

This question is related to python linux console terminal width

The answer is


I searched around and found a solution for windows at :

http://code.activestate.com/recipes/440694-determine-size-of-console-window-on-windows/

and a solution for linux here.

So here is a version which works both on linux, os x and windows/cygwin :

""" getTerminalSize()
 - get width and height of console
 - works on linux,os x,windows,cygwin(windows)
"""

__all__=['getTerminalSize']


def getTerminalSize():
   import platform
   current_os = platform.system()
   tuple_xy=None
   if current_os == 'Windows':
       tuple_xy = _getTerminalSize_windows()
       if tuple_xy is None:
          tuple_xy = _getTerminalSize_tput()
          # needed for window's python in cygwin's xterm!
   if current_os == 'Linux' or current_os == 'Darwin' or  current_os.startswith('CYGWIN'):
       tuple_xy = _getTerminalSize_linux()
   if tuple_xy is None:
       print "default"
       tuple_xy = (80, 25)      # default value
   return tuple_xy

def _getTerminalSize_windows():
    res=None
    try:
        from ctypes import windll, create_string_buffer

        # stdin handle is -10
        # stdout handle is -11
        # stderr handle is -12

        h = windll.kernel32.GetStdHandle(-12)
        csbi = create_string_buffer(22)
        res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
    except:
        return None
    if res:
        import struct
        (bufx, bufy, curx, cury, wattr,
         left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
        sizex = right - left + 1
        sizey = bottom - top + 1
        return sizex, sizey
    else:
        return None

def _getTerminalSize_tput():
    # get terminal width
    # src: http://stackoverflow.com/questions/263890/how-do-i-find-the-width-height-of-a-terminal-window
    try:
       import subprocess
       proc=subprocess.Popen(["tput", "cols"],stdin=subprocess.PIPE,stdout=subprocess.PIPE)
       output=proc.communicate(input=None)
       cols=int(output[0])
       proc=subprocess.Popen(["tput", "lines"],stdin=subprocess.PIPE,stdout=subprocess.PIPE)
       output=proc.communicate(input=None)
       rows=int(output[0])
       return (cols,rows)
    except:
       return None


def _getTerminalSize_linux():
    def ioctl_GWINSZ(fd):
        try:
            import fcntl, termios, struct, os
            cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,'1234'))
        except:
            return None
        return cr
    cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
    if not cr:
        try:
            fd = os.open(os.ctermid(), os.O_RDONLY)
            cr = ioctl_GWINSZ(fd)
            os.close(fd)
        except:
            pass
    if not cr:
        try:
            cr = (env['LINES'], env['COLUMNS'])
        except:
            return None
    return int(cr[1]), int(cr[0])

if __name__ == "__main__":
    sizex,sizey=getTerminalSize()
    print  'width =',sizex,'height =',sizey

Not sure why it is in the module shutil, but it landed there in Python 3.3, Querying the size of the output terminal:

>>> import shutil
>>> shutil.get_terminal_size((80, 20))  # pass fallback
os.terminal_size(columns=87, lines=23)  # returns a named-tuple

A low-level implementation is in the os module. Also works in Windows.

A backport is now available for Python 3.2 and below:


Code above didn't return correct result on my linux because winsize-struct has 4 unsigned shorts, not 2 signed shorts:

def terminal_size():
    import fcntl, termios, struct
    h, w, hp, wp = struct.unpack('HHHH',
        fcntl.ioctl(0, termios.TIOCGWINSZ,
        struct.pack('HHHH', 0, 0, 0, 0)))
    return w, h

hp and hp should contain pixel width and height, but don't.


Starting at Python 3.3 it is straight forward: https://docs.python.org/3/library/os.html#querying-the-size-of-a-terminal

>>> import os
>>> ts = os.get_terminal_size()
>>> ts.lines
24
>>> ts.columns
80

@reannual's answer works well, but there's an issue with it: os.popen is now deprecated. The subprocess module should be used instead, so here's a version of @reannual's code that uses subprocess and directly answers the question (by giving the column width directly as an int:

import subprocess

columns = int(subprocess.check_output(['stty', 'size']).split()[1])

Tested on OS X 10.9


Many of the Python 2 implementations here will fail if there is no controlling terminal when you call this script. You can check sys.stdout.isatty() to determine if this is in fact a terminal, but that will exclude a bunch of cases, so I believe the most pythonic way to figure out the terminal size is to use the builtin curses package.

import curses
w = curses.initscr()
height, width = w.getmaxyx()

Try "blessings"

I was looking for the very same thing. It is very easy to use and offers tools for coloring, styling and positioning in the terminal. What you need is as easy as:

from blessings import Terminal

t = Terminal()

w = t.width
h = t.height

Works like a charm in Linux. (I'm not sure about MacOSX and Windows)

Download and documentation here

or you can install it with pip:

pip install blessings

It looks like there are some problems with that code, Johannes:

  • getTerminalSize needs to import os
  • what is env? looks like os.environ.

Also, why switch lines and cols before returning? If TIOCGWINSZ and stty both say lines then cols, I say leave it that way. This confused me for a good 10 minutes before I noticed the inconsistency.

Sridhar, I didn't get that error when I piped output. I'm pretty sure it's being caught properly in the try-except.

pascal, "HHHH" doesn't work on my machine, but "hh" does. I had trouble finding documentation for that function. It looks like it's platform dependent.

chochem, incorporated.

Here's my version:

def getTerminalSize():
    """
    returns (lines:int, cols:int)
    """
    import os, struct
    def ioctl_GWINSZ(fd):
        import fcntl, termios
        return struct.unpack("hh", fcntl.ioctl(fd, termios.TIOCGWINSZ, "1234"))
    # try stdin, stdout, stderr
    for fd in (0, 1, 2):
        try:
            return ioctl_GWINSZ(fd)
        except:
            pass
    # try os.ctermid()
    try:
        fd = os.open(os.ctermid(), os.O_RDONLY)
        try:
            return ioctl_GWINSZ(fd)
        finally:
            os.close(fd)
    except:
        pass
    # try `stty size`
    try:
        return tuple(int(x) for x in os.popen("stty size", "r").read().split())
    except:
        pass
    # try environment variables
    try:
        return tuple(int(os.getenv(var)) for var in ("LINES", "COLUMNS"))
    except:
        pass
    # i give up. return default.
    return (25, 80)

Here is an version that should be Linux and Solaris compatible. Based on the posts and commments from madchine. Requires the subprocess module.

def termsize():
    import shlex, subprocess, re
    output = subprocess.check_output(shlex.split('/bin/stty -a'))
    m = re.search('rows\D+(?P\d+); columns\D+(?P\d+);', output)
    if m:
        return m.group('rows'), m.group('columns')
    raise OSError('Bad response: %s' % (output))
>>> termsize()
('40', '100')

If you're using Python 3.3 or above, I'd recommend the built-in get_terminal_size() as already recommended. However if you are stuck with an older version and want a simple, cross-platform way of doing this, you could use asciimatics. This package supports versions of Python back to 2.7 and uses similar options to those suggested above to get the current terminal/console size.

Simply construct your Screen class and use the dimensions property to get the height and width. This has been proven to work on Linux, OSX and Windows.

Oh - and full disclosure here: I am the author, so please feel free to open a new issue if you have any problems getting this to work.


use

import console
(width, height) = console.getTerminalSize()

print "Your terminal's width is: %d" % width

EDIT: oh, I'm sorry. That's not a python standard lib one, here's the source of console.py (I don't know where it's from).

The module seems to work like that: It checks if termcap is available, when yes. It uses that; if no it checks whether the terminal supports a special ioctl call and that does not work, too, it checks for the environment variables some shells export for that. This will probably work on UNIX only.

def getTerminalSize():
    import os
    env = os.environ
    def ioctl_GWINSZ(fd):
        try:
            import fcntl, termios, struct, os
            cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,
        '1234'))
        except:
            return
        return cr
    cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
    if not cr:
        try:
            fd = os.open(os.ctermid(), os.O_RDONLY)
            cr = ioctl_GWINSZ(fd)
            os.close(fd)
        except:
            pass
    if not cr:
        cr = (env.get('LINES', 25), env.get('COLUMNS', 80))

        ### Use get(key[, default]) instead of a try/catch
        #try:
        #    cr = (env['LINES'], env['COLUMNS'])
        #except:
        #    cr = (25, 80)
    return int(cr[1]), int(cr[0])

It's either:

import os
columns, rows = os.get_terminal_size(0)
# or
import shutil
columns, rows = shutil.get_terminal_size()

The shutil function is just a wrapper around os one that catches some errors and set up a fallback, however it has one huge caveat - it breaks when piping!, which is a pretty huge deal.
To get terminal size when piping use os.get_terminal_size(0) instead.

First argument 0 is an argument indicating that stdin file descriptor should be used instead of default stdout. We want to use stdin because stdout detaches itself when it is being piped which in this case raises an error.

I've tried to figure out when would it makes sense to use stdout instead of stdin argument and have no idea why it's a default here.


I was trying the solution from here that calls out to stty size:

columns = int(subprocess.check_output(['stty', 'size']).split()[1])

However this failed for me because I was working on a script that expects redirected input on stdin, and stty would complain that "stdin isn't a terminal" in that case.

I was able to make it work like this:

with open('/dev/tty') as tty:
    height, width = subprocess.check_output(['stty', 'size'], stdin=tty).split()

Examples related to python

programming a servo thru a barometer Is there a way to view two blocks of code from the same file simultaneously in Sublime Text? python variable NameError Why my regexp for hyphenated words doesn't work? Comparing a variable with a string python not working when redirecting from bash script is it possible to add colors to python output? Get Public URL for File - Google Cloud Storage - App Engine (Python) Real time face detection OpenCV, Python xlrd.biffh.XLRDError: Excel xlsx file; not supported Could not load dynamic library 'cudart64_101.dll' on tensorflow CPU-only installation

Examples related to linux

grep's at sign caught as whitespace How to prevent Google Colab from disconnecting? "E: Unable to locate package python-pip" on Ubuntu 18.04 How to upgrade Python version to 3.7? Install Qt on Ubuntu Get first line of a shell command's output Cannot connect to the Docker daemon at unix:/var/run/docker.sock. Is the docker daemon running? Run bash command on jenkins pipeline How to uninstall an older PHP version from centOS7 How to update-alternatives to Python 3 without breaking apt?

Examples related to console

Error in MySQL when setting default value for DATE or DATETIME Where can I read the Console output in Visual Studio 2015 Chrome - ERR_CACHE_MISS Swift: print() vs println() vs NSLog() Datatables: Cannot read property 'mData' of undefined How do I write to the console from a Laravel Controller? Cannot read property 'push' of undefined when combining arrays Very simple log4j2 XML configuration file using Console and File appender Console.log not working at all Chrome: console.log, console.debug are not working

Examples related to terminal

Git is not working after macOS Update (xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools) Can't compile C program on a Mac after upgrade to Mojave Flutter command not found VSCode Change Default Terminal How to switch Python versions in Terminal? How to open the terminal in Atom? Color theme for VS Code integrated terminal How to edit a text file in my terminal How to open google chrome from terminal? Switch between python 2.7 and python 3.5 on Mac OS X

Examples related to width

How to Determine the Screen Height and Width in Flutter Change New Google Recaptcha (v2) Width How to get the <td> in HTML tables to fit content, and let a specific <td> fill in the rest Full width image with fixed height Width equal to content CSS to make table 100% of max-width CSS Input with width: 100% goes outside parent's bound HTML email in outlook table width issue - content is wider than the specified table width How to set up fixed width for <td>? How to make div's percentage width relative to parent div and not viewport