python - 在python中,如何获取控制台窗口宽度

在python中有没有方法以编程方式确定控制台的宽度? 我指的是字符数,不是窗口的像素宽度。

我正在寻找一个在Linux上工作的解决方案

时间:


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

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

这不是python标准库,这里是console.py (我不知道是从哪里来)的源代码。

模块的工作方式如下: 它检查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])


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

我搜索了一下,找到了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


>>> import shutil
>>> shutil.get_terminal_size((80, 20))
os.terminal_size(columns=87, lines=23)


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

看起来代码有问题,Johannes:

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

还有,为什么在返回之前 switch linescols? 如果 TIOCGWINSZstty 都说 lines,那么 cols,我说 leave 。 在我注意到不一致的情况下,这就会让我觉得一个好的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)

从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

我正在尝试调用stty size的解决方案:


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

失败了,因为我在使用一个预定重定向输入的脚本,stty会抱怨"stdin不是终端"

我可以让它像这样工作:


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

这是一个Linux和Solaris兼容的版本,需要子进程模块。

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


import subprocess

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

在操作系统X10.9上测试过

...