How to get Linux console window width in Python

后端 未结 14 1136
清歌不尽
清歌不尽 2020-11-22 15:08

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

相关标签:
14条回答
  • 2020-11-22 15:14

    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.

    0 讨论(0)
  • 2020-11-22 15:14

    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.

    0 讨论(0)
  • 2020-11-22 15:15

    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.

    0 讨论(0)
  • 2020-11-22 15:16

    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()
    
    0 讨论(0)
  • 2020-11-22 15:18

    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])
    
    0 讨论(0)
  • 2020-11-22 15:20

    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)
    
    0 讨论(0)
提交回复
热议问题