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


264

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

编辑

寻找适用于Linux的解决方案


请看此答案,以获得更广泛的解决方案,以具有“列相关”的打印机制。stackoverflow.com/questions/44129613/...
里卡多Petraglia

Answers:


261
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的回答)


1
如果通过“ -a”而不是“ size”,也可以使它在Solaris上也可以使用。在以分号分隔的输出中将有“行= Y;列= X”。
约瑟夫·加文

5
在Bash中,默认情况下未导出COLUMNS,这就是为什么os.environ [“ COLUMNS”]不起作用的原因。
Kjell Andreassen

38
rows, columns = subprocess.check_output(['stty', 'size']).split()会短一些,再加上子流程就是未来
cdosborn 2015年

7
tsty比stty好,因为stty无法与PIPE一起使用。
liuyang15年

5
rows, columns = subprocess.check_output(['stty', 'size']).decode().split()如果您想要py2 / 3兼容的unicode字符串
Bryce Guinta'8

263

不知道为什么它在模块中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及以下版本:


25
那是因为您不应该再使用2.7了,将其跳到3.x是值得的。
anthonyryan1 2015年

5
@osirisgothra许多托管提供程序尚不支持python3,因此我们中有些人被迫使用python2进行后端开发。尽管这与获得终端大小无关……
白胡子

4
@osirisgothra因为有很多Python 2代码需要太多的工作才能移植。而且,Pypy对Python 3的支持仍然很差。

2
@whitebeard订阅者是否有理由无法在VPS上安装Python 3?还是在2016年,人们是否仍在使用其管理员不愿意安装Python 3的共享托管?例如,已python3.5安装WebFaction共享托管。
Damian Yerrick '16

2
+1是一种解决方案,当标准输入已从文件重定向时,该解决方案也适用!与其他解决方案,我要么让Inappropriate ioctl for device错误/警告,或获得的80所定义的回退值
pawamoy

65

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

5
感谢您的快速答复,但在此处(effbot.org/zone/console-handbook.htm)中,它表示“控制台模块当前仅适用于Windows 95、98,NT和2000。” 我正在寻找一种适用于Linux的解决方案。从标记中可能不清楚,我将相应地编辑问题。
谢尔盖·戈洛维琴科

2
由于您使用的“控制台”模块不在标准的python库中,因此您应提供其源代码或至少一个链接。
nosklo

对此我感到很抱歉。实际上,我不知道那个模块。我尝试了导入控制台,它可以正常工作,我使用了console。<tab> <tab>和getTerminalSize()出现了。我已经发布了答案,因为我对简单性g
Johannes Weiss

我可能正在查看其他“控制台”模块,请为您提供的模块提供一个链接吗?
谢尔盖·戈洛维琴科

4
哦,不要堆在代码上,但是“ cr”是一个令人困惑的名称,因为它暗示元组是(cols,行)。实际上,情况恰恰相反。
Paul Du Bois

57

上面的代码未在我的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应该包含像素的宽度和高度,但不包含。


4
这就是应该做的事;请注意,如果打算打印到终端,则应使用“ 1”作为文件描述符(ioctl的第一个参数),因为stdin可能是管道或其他tty。
mic_e

1
也许0应该替换为fcntl.ioctl(sys.stdin.fileno(), ...
raylu

4
这是最好的答案-您的用户会很高兴没有意外的子过程正在发生,只是为了获得术语宽度
zzzeek 2013年

4
这确实是最干净的答案。我认为您应该使用stdoutstderr代替stdinstdin可能是一个管道。您可能还需要添加一行,例如if not os.isatty(0): return float("inf")
mic_e 2014年

这以某种方式在没有功能的chromebook终端上起作用。+1
HyperNeutrino

39

我四处搜寻,找到了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

您自己为我节省了时间。在Linux上工作。也应该在Windows上工作。谢谢!
史蒂夫五世

30

要么是:

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参数有意义,并且不知道为什么它是默认值。


3
os.get_terminal_size()是在Python 3.3中引入的
villapx

我起初尝试了shutil,但第一次尝试却奏效了。我正在使用Python 3.6+。

os.get_terminal_size(0)如果通过管道传输到stdin,使用将崩溃。试试:echo x | python3 -c 'import os; print(os.get_terminal_size(0))'
ehabkost


6

该代码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)

我也是在徘徊env,确实是env = os.environ公认的答案中得出的
sdaau,

6

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

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

2

我正在尝试从此处调用的解决方案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()

1

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

import subprocess

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

在OS X 10.9上测试


1

尝试“祝福”

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

from blessings import Terminal

t = Terminal()

w = t.width
h = t.height

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

在此处下载和文档

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

pip install blessings

如今,我会说尝试“祝福”,这是“祝福”的当前维护(和增强)。
zezollo

1

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

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

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


0

这是应该与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”)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.