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

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

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

Ищете решение, которое работает на Linux

15 ответов

Решение
import os
rows, columns = os.popen('stty size', 'r').read().split()

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

В отличие от значения os.environ["COLUMNS"] (к которому я не могу получить доступ, несмотря на использование bash в качестве стандартной оболочки), данные также будут современными, тогда как я считаю, что os.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

Низкоуровневая реализация находится в модуле os.

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

Использование

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

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

Это либо:

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, и понятия не имею, почему это значение по умолчанию.

Я искал вокруг и нашел решение для 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://stackru.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

>>> 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" делает. У меня были проблемы с поиском документации для этой функции. Похоже, это зависит от платформы.

Хохем, включены.

Вот моя версия:

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

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

Попробуйте "благословения"

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

from blessings import Terminal

t = Terminal()

w = t.width
h = t.height

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

Скачать и документацию здесь

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

pip install blessings

Я пробовал решение отсюда, которое призывает 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()

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

import subprocess

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

Протестировано на OS X 10.9

Использоватьsubprocess, это наиболее удобный способ сделать это:

Импорт:

      import subprocess

Пример использования:

      print(subprocess.check_output(['stty', 'size']).split())

Примечание. Эта функция возвращает байты, но вы можете привести ее к целому числу с помощьюint()функция.

Примечание. Эта функция возвращает массив следующего вида:array[0]= строки иarray[1]= столбцы.

Выход:

      [b'46', b'188']

Например, если вам нужно сравнить, превышает ли ширина вашей консолиW, вы можете сделать что-то вроде этого:

      if int(subprocess.check_output(['stty', 'size']).split()[1]) > W:
    ...

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

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

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

Вот версия, которая должна быть совместима с Linux и Solaris. Основано на сообщениях и комментариях от brokkr. Требуется модуль подпроцесса.

def termize():
    импорт шлекс, подпроцесс, ре
    output = subprocess.check_output(shlex.split('/bin/stty -a'))
    m = re.search('строки \D+(?P\d+); столбцы \D+(?P\d+);', выходные данные)
    если М:
        вернуть m.group("строки"), m.group("столбцы")
    поднять OSError('Неверный ответ: %s' % (вывод))
>>> termize()
("40", "100")
Другие вопросы по тегам