如何在Python中获取Linux控制台窗口宽度

问题:如何在Python中获取Linux控制台窗口宽度

python中有没有办法以编程方式确定控制台的宽度?我的意思是不换行就适合一行的字符数,而不是窗口的像素宽度。

编辑

寻找适用于Linux的解决方案

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


回答 0

import os
rows, columns = os.popen('stty size', 'r').read().split()

使用“ stty size”命令,该命令根据python邮件列表上的线程在linux上相当普遍。它以文件形式打开“ stty size”命令,并从中“读取”,并使用简单的字符串拆分来分隔坐标。

与os.environ [“ COLUMNS”]值(尽管使用bash作为我的标准Shell不能访问)不同,数据也将是最新的,而我相信os.environ [“ COLUMNS”]该值仅在python解释器启动时有效(假设从那时起用户调整了窗口的大小)。

(有关如何在python 3.3+上执行此操作,请参见@GringoSuave的回答)

import os
rows, columns = os.popen('stty size', 'r').read().split()

uses the ‘stty size’ command which according to a thread on the python mailing list is reasonably universal on linux. It opens the ‘stty size’ command as a file, ‘reads’ from it, and uses a simple string split to separate the coordinates.

Unlike the os.environ[“COLUMNS”] value (which I can’t access in spite of using bash as my standard shell) the data will also be up-to-date whereas I believe the os.environ[“COLUMNS”] value would only be valid for the time of the launch of the python interpreter (suppose the user resized the window since then).

(See answer by @GringoSuave on how to do this on python 3.3+)


回答 1

不知道为什么它在模块中shutil,但是它在Python 3.3中降落在那里,查询输出终端的大小

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

os模块中有一个底层实现。在Windows中也可以使用。

反向移植现在可用于Python 3.2及以下版本:

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:


回答 2

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

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

编辑:哦,对不起。那不是python标准库,这是console.py的来源(我不知道它来自哪里)。

该模块似乎像这样工作:检查是否termcap可用,何时可用。它使用它;如果不是,它将检查终端是否支持特殊ioctl调用,并且该调用也不起作用,它将检查某些shell为此导出的环境变量。这可能仅适用于UNIX。

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])

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])

回答 3

上面的代码未在我的Linux上返回正确的结果,因为winsize-struct有4条未签名的短裤,而不是2条已签名的短裤:

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和hp应该包含像素的宽度和高度,但不包含。

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.


回答 4

我四处搜寻,找到了Windows的解决方案:

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

以及适用于Linux的解决方案。

所以这是一个可以在linux,os x和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

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

回答 5

要么是:

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

shutil函数只是一个包装器,os可以捕获一些错误并设置后备功能,但是它有一个巨大的警告- 在管道传输时会中断!,这是一笔不小的数目。
要获得配管时的端子尺寸,请os.get_terminal_size(0)改用。

第一个参数0是指示应使用stdin文件描述符而不是默认stdout的参数。我们要使用stdin,因为stdout在被管道传输时会自行分离,在这种情况下会引发错误。

我试图弄清楚什么时候使用stdout代替stdin参数有意义,并且不知道为什么它是默认值。

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.


回答 6

从Python 3.3开始,它很简单: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

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

回答 7

该代码Johannes似乎有一些问题:

  • getTerminalSize 需要 import os
  • 是什么env?看起来像os.environ

另外,为什么要切换linescols返回之前?如果TIOCGWINSZ并且stty都说lines那么cols,我说就这样吧。在我注意到不一致之前,这使我困惑了10分钟。

Sridhar,通过管道输出时没有出现该错误。我很确定它会被try-except正确捕获。

pascal,"HHHH"在我的机器上"hh"不起作用,但是可以。我在查找该功能的文档时遇到了麻烦。看起来它与平台有关。

chochem,注册成立。

这是我的版本:

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)

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)

回答 8

如果在调用此脚本时没有控制终端,则此处的许多Python 2实现将失败。您可以检查sys.stdout.isatty()来确定这是否实际上是一个终端,但这将排除一堆情况,因此我认为找出终端大小的最有效方法是使用内置的curses包。

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

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()

回答 9

我正在尝试从此处调用的解决方案stty size

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

但是,这对我来说失败了,因为我正在处理一个脚本,该脚本期望stdin上的重定向输入,并且stty在这种情况下会抱怨“ stdin不是终端”。

我能够使它像这样工作:

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

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()

回答 10

@reannual的答案很好用,但是有一个问题:os.popen 现在已弃用。该subprocess模块应改为使用,所以这里的@ reannual的代码,使用一个版本subprocess,直接回答了这个问题(通过直接向列宽为int

import subprocess

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

在OS X 10.9上测试

@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


回答 11

尝试“祝福”

我一直在寻找同样的东西。它非常易于使用,并提供用于在终端上进行着色,样式和定位的工具。您所需要的很简单:

from blessings import Terminal

t = Terminal()

w = t.width
h = t.height

在Linux中像灵符一样工作。(我不确定MacOSX和Windows)

在此处下载和文档

或者您可以使用pip安装它:

pip install blessings

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

回答 12

如果您使用的是Python 3.3或更高版本,建议您get_terminal_size()按照已推荐的内置方法进行操作。但是,如果您坚持使用较旧的版本,并希望通过一种简单的跨平台方法来执行此操作,则可以使用asciimatics。该软件包支持Python 2.7之前的版本,并使用与上面建议的选项类似的选项来获取当前的终端/控制台大小。

只需构造您的Screen类并使用该dimensions属性即可获取高度和宽度。它已被证明可以在Linux,OSX和Windows上运行。

哦,这里有完整的披露:我是作者,因此,如果您有任何疑问,请随时打开一个新期刊。

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.


回答 13

这是应该与Linux和Solaris兼容的版本。基于madchine的帖子和评论。需要子流程模块。

def termsize():
    导入shlex,子流程,重新
    输出= subprocess.check_output(shlex.split('/ bin / stty -a'))
    m = re.search('rows \ D +(?P \ d +); column \ D +(?P \ d +);',输出)
    如果m:
        返回m.group('rows'),m.group('columns')
    引发OSError('错误响应:%s'%(输出))
>>> termize()
(“ 40”,“ 100”)

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')