Як отримати ширину вікна консолі Linux у Python


264

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

Редагувати

Шукаєте рішення, яке працює в Linux


Подивіться на цю відповідь для більш масштабного рішення, щоб мати механізм друку "залежно від стовпців". stackoverflow.com/questions/44129613 / ...
Ріккардо Petraglia

Відповіді:


261
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 (припустимо, користувач змінив розмір вікна з тих пір).

(Див. Відповідь @GringoSuave про те, як це зробити на python 3.3+)


1
Ви можете змусити це працювати і на Solaris, якщо замість "size" ви передасте "-a". У розділі крапки з комою буде розміщено "рядки = Y; стовпці = X".
Джозеф Гарвін

5
COLUMNS не експортується за замовчуванням у Bash, тому os.environ ["COLUMNS"] не працює.
Kjell Andreassen

38
rows, columns = subprocess.check_output(['stty', 'size']).split()трохи коротше, плюс підпроцес - майбутнє
cdosborn

7
tput краще, ніж stty, оскільки stty не може працювати з PIPE.
liuyang1

5
rows, columns = subprocess.check_output(['stty', 'size']).decode().split()Якщо ви хочете, щоб рядки Unicode для сумісності з py2 / 3
Bryce Guinta,

264

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

5
@osirisgothra Багато хостинг-провайдерів ще не підтримують python3, тому деякі з нас змушені використовувати python2 для розвитку заднього кінця. Хоча це не має нічого спільного з отриманням розміру терміналу ...
біла борода

4
@osirisgothra Оскільки існує багато коду Python 2, який би зайняв занадто багато роботи для порту. Крім того, у Pypy все ще є досить бідна підтримка Python 3.
Сурма

2
@whitebeard Чи є причина, що абонент не може встановити Python 3 на VPS? Або в 2016 році люди все ще використовують спільний хостинг, адміністратор якого не бажає встановлювати Python 3? Наприклад, встановлено спільний хостинг WebFaction python3.5.
Даміян Єррік

2
+1 для рішення, яке також працює, коли стандартне введення було перенаправлено з файлу! З іншими рішеннями я або отримував Inappropriate ioctl for deviceпомилки / попередження, або отримував визначене резервне значення 80.
pawamoy

65

використання

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

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

РЕДАКТ : о, пробач. Це не стандартна пітонова версія, ось джерело 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])

5
Дякуємо за швидку відповідь, але тут ( effbot.org/zone/console-handbook.htm ) сказано, що "Модуль консолі наразі доступний лише для Windows 95, 98, NT та 2000." Я шукаю рішення, яке працює в Linux. Це, мабуть, було незрозумілим із тегу, я відповідно відредагую питання.
Сергій Головченко

2
оскільки цей "консольний" модуль, який ви використовуєте, відсутній у стандартній бібліотеці python, вам слід надати його вихідний код або принаймні посилання на нього.
nosklo

Мені так шкода. Насправді я не знав цього модуля. Я спробував консоль імпорту, і вона працювала, я використовував консоль. <tab> <tab> і getTerminalSize () з'явився. Замість того, щоб дивитись, звідки я, я вже розмістив відповідь, тому що мені так пощастило простота г
Йоганнес Вайс

Я, можливо, дивлюся на інший "консольний" модуль, чи можете ви надати посилання на той, який у вас є?
Сергій Головченко

4
О, і не купувати код, але "cr" - це заплутане ім'я, тому що це означає, що кортеж є (cols, рядки). Насправді це навпаки.
Пол Дю Буа

57

Код вище не повернув правильний результат на моєму Linux, оскільки winize-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

л.с. та к.с. повинні містити ширину та висоту пікселів, але не слід.


4
Ось як це слід робити; зауважте, що якщо ви збираєтесь надрукувати на терміналі, ви повинні використовувати "1" як дескриптор файлу (перший аргумент ioctl), оскільки stdin може бути трубою або деякими різними tty.
mic_e

1
Можливо, 0 слід замінити наfcntl.ioctl(sys.stdin.fileno(), ...
raylu

4
це найкраща відповідь - ваші користувачі будуть раді, що не буде несподіваного підпроцесу, який відбувається лише для отримання ширини терміну
zzzeek

4
це справді найчистіша відповідь. Я думаю, ви повинні використовувати stdoutабо stderrзамість цього stdin. stdinможе бути дуже трубою. Ви також можете додати рядок типу if not os.isatty(0): return float("inf").
mic_e

це якимось чином працює на терміналі 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

Я спробував шутил спочатку, і це спрацювало з першої спроби. Я використовую Python 3.6+.
Дан

Використання os.get_terminal_size(0)призведе до виходу з ладу, якщо ви покладете на stdin. Спробуйте:echo x | python3 -c 'import os; print(os.get_terminal_size(0))'
ehabkost

19

Починаючи з 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

16
shutil.get_terminal_size() is the high-level function which should normally be used, os.get_terminal_size is the low-level implementation.
mid_kid

1
Це майже копія відповіді на рік, постаріла вище.
Грінго Суаве

6

Схоже, з цим кодом є деякі проблеми, Йоганнес:

  • getTerminalSize потребує import os
  • що таке env? схоже os.environ.

Крім того, навіщо перемикатися linesі colsперед поверненням? Якщо TIOCGWINSZі sttyте , і інше кажуть linesтоді cols, я кажу, залиште це так. Це заплутало мене на добрі 10 хвилин, перш ніж я помітив невідповідність.

Шрідхар, я не отримав цієї помилки, коли передавав трубопровід. Я майже впевнений, що його правильно потрапляють у випробування.

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

2

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

Я шукав саме те саме. Це дуже простий у використанні і пропонує інструменти для фарбування, стилізації та позиціонування в терміналі. Вам потрібно так само просто, як:

from blessings import Terminal

t = Terminal()

w = t.width
h = t.height

Працює як шарм в Linux. (Я не впевнений у MacOSX та Windows)

Завантажити та документацію тут

або ви можете встановити його за допомогою pip:

pip install blessings

Сьогодні я б сказав спробувати "благословенний", який є збереженою на даний момент виделкою (і розширенням) "благословення".
зезолло

1

@ rean year відповідь працює добре, але проблема з цим: os.popen зараз застаріла . subprocessМодуль повинен використовуватися замість цього, так ось версія @ reannual код , який використовує subprocessі безпосередньо відповідає на питання (даючи ширинустовпця безпосередньо як int:

import subprocess

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

Тестовано на OS X 10.9


1

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

Просто побудуйте свій Screenклас та використовуйте dimensionsвластивість, щоб отримати висоту та ширину. Це було доведено, що він працює в Linux, OSX та Windows.

О - і повне розкриття тут: я автор, тому, будь ласка, не соромтесь відкривати нове видання, якщо у вас виникнуть проблеми з тим, як це зробити.


0

Ось версія, яка повинна бути сумісною для Linux та Solaris. На основі публікацій та коментарів з Madchine . Потрібен модуль підпроцесу.

def terminize ():
    імпорт shlex, підпроцес, повторно
    output = subprocess.check_output (shlex.split ('/ bin / stty -a'))
    m = re.search ('рядки \ D + (? p \ d +); стовпці \ D + (? p \ d +);', вихід)
    якщо м:
        повернути m.group ('рядки'), m.group ('стовпці')
    підняти OSError ('Погана відповідь:% s'% (вихід))
>>> терміни ()
('40', '100')
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.