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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/566746/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 17:01:38  来源:igfitidea点击:

How to get Linux console window width in Python

pythonlinuxconsoleterminalwidth

提问by Sergey Golovchenko

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.

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

Edit

编辑

Looking for a solution that works on Linux

寻找适用于 Linux 的解决方案

采纳答案by brokkr

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 listis 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.

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

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

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

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

(请参阅@GringoSuave 关于如何在 python 3.3+ 上执行此操作的答案)

回答by Johannes Weiss

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

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

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

该模块似乎是这样工作的:它检查是否termcap可用,当是时。它使用那个; 如果否,它会检查终端是否支持特殊ioctl调用并且这也不起作用,它会检查某些外壳为此导出的环境变量。这可能仅适用于 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])

回答by pascal

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

上面的代码在我的 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 and hp should contain pixel width and height, but don't.

hp 和 hp 应该包含像素宽度和高度,但不要。

回答by thejoshwolfe

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

看起来那个代码有一些问题,约翰内斯:

  • getTerminalSizeneeds to import os
  • what is env? looks like os.environ.
  • getTerminalSize需要 import os
  • 什么是env?看起来像os.environ

Also, why switch linesand colsbefore returning? If TIOCGWINSZand sttyboth say linesthen cols, I say leave it that way. This confused me for a good 10 minutes before I noticed the inconsistency.

另外,为什么在返回之前切换linescols?如果TIOCGWINSZstty两者都说linesthen cols,我说就这样吧。在我注意到不一致之前,这让我困惑了 10 分钟。

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

Sridhar,当我通过管道输出时,我没有收到那个错误。我很确定它在 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.

pascal,"HHHH"在我的机器上"hh"不起作用,但是可以。我无法找到该功能的文档。看起来它依赖于平台。

chochem, incorporated.

chochem, 合并

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)

回答by Derrick Petzold

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

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

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

回答by Harco Kuppens

I searched around and found a solution for windows at :

我四处搜索并在以下位置找到了适用于 Windows 的解决方案:

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

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

and a solution for linux here.

以及这里的 linux 解决方案。

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

所以这里有一个适用于 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

回答by Gringo Suave

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

不知道为什么它在模块中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

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

os 模块中有一个低级实现。也适用于 Windows。

A backport is now available for Python 3.2 and below:

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

回答by Bob Enohp

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

从 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

回答by rickcnagy

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

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

import subprocess

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

Tested on OS X 10.9

在 OS X 10.9 上测试

回答by Marc Liyanage

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

我正在尝试从这里调用的解决方案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 sttywould complain that "stdin isn't a terminal" in that case.

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

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