Как получить ширину окна консоли Linux в Python

есть ли способ в python программно определить ширину консоли? Я имею в виду количество символов, которые помещаются в одну строку без упаковки, а не ширину пикселя окна.

редактировать

поиск решения, которое работает на Linux

14 ответов


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

использует команду "stty size", которая в соответствии с поток в списке рассылки python достаточно универсален в linux. Он открывает команду "stty size" в виде файла, "читает" из него и использует простое разделение строк для разделения координат.

в отличие от операционной системы.значение environ["COLUMNS"] (к которому я не могу получить доступ, несмотря на использование bash в качестве стандартной оболочки) данные также будут актуальными, тогда как я считаю, что ОС.значение environ ["COLUMNS"] будет только действителен на момент запуска интерпретатора python (предположим, что с тех пор пользователь изменил размер окна).


не уверен, почему он находится в модуле 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

реализация низкого уровня находится в модуле ОС.

backport теперь доступен для Python 3.2 и ниже:


использовать

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

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

редактировать: о, извините. Это не стандартный lib 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])

код выше не вернул правильный результат в моем 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 должны содержать ширину и высоту пикселей, но не.


я поискал вокруг и нашел решение для 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

начиная с 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

похоже, есть некоторые проблемы с этим кодом, Иоганнес:

  • getTerminalSize нужно import os
  • что это env? похоже на os.environ.

кроме того, зачем переходить lines и cols перед возвращением? Если TIOCGWINSZ и stty как сказать lines затем cols, Я говорю, оставьте это так. Это смутило меня в течение добрых 10 минут, прежде чем я заметил несоответствие.

Шридхар, я не получил эту ошибку, когда я по трубе выход. Я почти уверен, что он пойман должным образом в попытке-за исключением.

Паскаль, "HHHH" не работает на моей машине, но "hh" делает. У меня были проблемы с поиском документации для этой функции. Похоже, это зависит от платформы.

chochem, incorporated.

вот мой вариант:

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 2 здесь потерпят неудачу, если нет управляющего терминала при вызове этого скрипта. Вы можете проверить sys.стандартный вывод.isatty (), чтобы определить, действительно ли это терминал, но это исключит кучу случаев, поэтому я считаю, что самый питонический способ выяснить размер терминала-использовать встроенный пакет проклятий.

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

либо:

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

на shutil функция-это просто оболочка вокруг os тот, который ловит некоторые ошибки и настраивает резерв, однако у него есть одно огромное предостережение -он ломается, когда трубопроводы!, что довольно большое дело.
Чтобы получить размер терминала при использовании трубопроводов .

первый аргумент 0 является аргументом, указывающим, что файловый дескриптор stdin должен использоваться вместо stdout по умолчанию. Мы хотим использовать stdin, потому что stdout отсоединяется, когда он передается по конвейеру, который в этом случае вызывает ошибку..
Я попытался выяснить, когда имеет смысл использовать stdout вместо аргумента stdin и понятия не имею, почему он здесь по умолчанию.


ответ@reannual работает хорошо, но с ним есть проблема:os.popen в настоящее время осуждается. The , поэтому вот версия кода @reannual, которая использует subprocess и непосредственно отвечает на вопрос (давая ширину столбца непосредственно как int:

import subprocess

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

протестировано на OS X 10.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()

попробовать "благословения"

Я искал то же самое. Он очень прост в использовании и предлагает инструменты для окраски, укладки и позиционирования в терминале. То, что вам нужно, так же просто, как:

from blessings import Terminal

t = Terminal()

w = t.width
h = t.height

работает как шарм в Linux. (Я не уверен в MacOSX и Windows)

загрузка и документация здесь

или вы можете установить его с помощью pip:

pip install blessings

Если вы используете Python 3.3 или выше, я бы рекомендовал встроенный get_terminal_size() Как уже было рекомендовано. Однако, если вы застряли с более старой версией и хотите простой, кросс-платформенный способ сделать это, вы можете использовать asciimatics. Этот пакет поддерживает версии Python до 2.7 и использует аналогичные опции, предложенные выше, чтобы получить текущий размер терминала / консоли.

просто построить свой Screen классе и использовать dimensions свойство для получения высоты и ширина. Это было доказано, чтобы работать на Linux, OSX и Windows.

О-и полное раскрытие здесь: я автор, поэтому, пожалуйста, не стесняйтесь открывать новый выпуск, Если у вас есть какие-либо проблемы с получением этой работы.


вот версия, которая должна быть совместима с Linux и Solaris. На основе постов и commments от 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')