So erhalten Sie die Fensterbreite der Linux-Konsole in Python


263

Gibt es in Python eine Möglichkeit, die Breite der Konsole programmgesteuert zu bestimmen? Ich meine die Anzahl der Zeichen, die in eine Zeile ohne Umbruch passen, nicht die Pixelbreite des Fensters.

Bearbeiten

Auf der Suche nach einer Lösung, die unter Linux funktioniert


In dieser Antwort finden Sie eine umfassendere Lösung für einen "spaltenabhängigen" Druckmechanismus. stackoverflow.com/questions/44129613/…
Riccardo Petraglia

Antworten:


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

verwendet den Befehl 'stty size', der laut einem Thread auf der Python-Mailingliste unter Linux einigermaßen universell ist. Es öffnet den Befehl 'stty size' als Datei, 'liest' daraus und verwendet eine einfache Zeichenfolgenaufteilung, um die Koordinaten zu trennen.

Im Gegensatz zum Wert von os.environ ["COLUMNS"] (auf den ich trotz Verwendung von bash als Standard-Shell nicht zugreifen kann) sind die Daten auch aktuell, während ich glaube, dass der Wert von os.environ ["COLUMNS"] Der Wert ist nur für den Zeitpunkt des Starts des Python-Interpreters gültig (vorausgesetzt, der Benutzer hat die Größe des Fensters seitdem geändert).

(Siehe Antwort von @GringoSuave, wie dies unter Python 3.3+ funktioniert.)


1
Sie können dies auch unter Solaris zum Laufen bringen, wenn Sie anstelle von "Größe" "-a" übergeben. In der durch Semikolons getrennten Ausgabe wird "Zeilen = Y; Spalten = X" angezeigt.
Joseph Garvin

5
COLUMNS wird in Bash nicht standardmäßig exportiert, deshalb funktioniert os.environ ["COLUMNS"] nicht.
Kjell Andreassen

38
rows, columns = subprocess.check_output(['stty', 'size']).split()ist etwas kürzer, und Teilprozess ist die Zukunft
cdosborn

7
tput ist besser als stty, da stty nicht mit PIPE arbeiten kann.
Liuyang1

5
rows, columns = subprocess.check_output(['stty', 'size']).decode().split()Wenn Sie Unicode-Zeichenfolgen für py2 / 3-Kompatibilität wünschen
Bryce Guinta

262

Ich bin mir nicht sicher, warum es sich im Modul befindet shutil, aber es ist dort in Python 3.3 gelandet. Abfragen der Größe des Ausgabeterminals :

>>> import shutil
>>> shutil.get_terminal_size((80, 20))  # pass fallback
os.terminal_size(columns=87, lines=23)  # returns a named-tuple

Eine Implementierung auf niedriger Ebene befindet sich im OS-Modul. Funktioniert auch unter Windows.

Für Python 3.2 und niedriger ist jetzt ein Backport verfügbar:


25
Das liegt daran, dass Sie 2.7 nicht mehr verwenden sollten. Machen Sie den Sprung zu 3.x, es lohnt sich.
anthonyryan1

5
@osirisgothra Viele Hosting-Anbieter unterstützen Python3 noch nicht, daher sind einige von uns gezwungen, Python2 für die Back-End-Entwicklung zu verwenden. Obwohl das nichts damit zu tun haben sollte, Terminalgröße zu bekommen ...
Whitebeard

4
@osirisgothra Weil es viel Python 2-Code gibt, dessen Portierung zu viel Arbeit kosten würde. Außerdem hat Pypy immer noch eine eher schlechte Python 3-Unterstützung.
Antimon

2
@whitebeard Gibt es einen Grund, warum der Abonnent Python 3 nicht auf einem VPS installieren kann? Oder verwenden 2016 noch Benutzer Shared Hosting, dessen Administrator Python 3 nicht installieren möchte? Beispielsweise wurde WebFaction Shared Hosting python3.5installiert.
Damian Yerrick

2
+1 für eine Lösung, die auch funktioniert, wenn die Standardeingabe aus einer Datei umgeleitet wurde! Bei anderen Lösungen erhielt ich entweder Inappropriate ioctl for deviceFehler / Warnungen oder den definierten Fallback-Wert von 80.
Pawamoy

65

verwenden

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

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

EDIT : Oh, tut mir leid. Das ist keine Python-Standardbibliothek, hier ist die Quelle von console.py (ich weiß nicht, woher es kommt).

Das Modul scheint so zu funktionieren: Es prüft, ob termcapes verfügbar ist, wenn ja. Es benutzt das; Wenn nein, wird geprüft, ob das Terminal einen speziellen ioctlAufruf unterstützt, und dies funktioniert auch nicht. Es wird nach Umgebungsvariablen gesucht, die einige Shells dafür exportieren. Dies funktioniert wahrscheinlich nur unter 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
Vielen Dank für die schnelle Antwort, aber hier ( effbot.org/zone/console-handbook.htm ) heißt es: "Das Konsolenmodul ist derzeit nur für Windows 95, 98, NT und 2000 verfügbar." Ich suche nach einer Lösung, die unter Linux funktioniert. Es war wahrscheinlich nicht klar aus dem Tag, ich werde die Frage entsprechend bearbeiten.
Sergey Golovchenko

2
Da sich dieses von Ihnen verwendete "Konsolen" -Modul nicht in der Standard-Python-Bibliothek befindet, sollten Sie den Quellcode oder zumindest einen Link dazu angeben.
Nosklo

Das tut mir so leid. Tatsächlich kannte ich dieses Modul nicht. Ich habe versucht, die Konsole zu importieren, und es hat funktioniert. Ich habe die Konsole verwendet. <Tab> <tab> und getTerminalSize () wurden angezeigt. Statt, wo es aus ich bereits eine Antwort geschrieben , weil ich so viel Glück der Einfachheit war g
Johannes Weiss

Ich könnte mir ein anderes "Konsolen" -Modul ansehen. Könnten Sie bitte einen Link für das Modul angeben, das Sie haben?
Sergey Golovchenko

4
Oh, und nicht auf den Code zu stapeln, aber "cr" ist ein verwirrender Name, weil er impliziert, dass das Tupel ist (Spalten, Zeilen). In Wirklichkeit ist es umgekehrt.
Paul Du Bois

57

Der obige Code hat unter Linux kein korrektes Ergebnis zurückgegeben, da wonize-struct 4 nicht signierte Shorts und nicht 2 signierte Shorts enthält:

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 und HP sollten Pixelbreite und -höhe enthalten, aber nicht.


4
So sollte es gemacht werden; Beachten Sie, dass Sie, wenn Sie auf dem Terminal drucken möchten, '1' als Dateideskriptor verwenden sollten (erstes Argument von ioctl), da stdin möglicherweise eine Pipe oder eine andere tty ist.
mic_e

1
Vielleicht sollte die 0 durchfcntl.ioctl(sys.stdin.fileno(), ...
raylu

4
Dies ist die beste Antwort - Ihre Benutzer werden froh sein, dass kein überraschender Teilprozess stattfindet, nur um die Laufzeit zu erhalten
zzzeek

4
Dies ist in der Tat die sauberste Antwort. Ich denke, Sie sollten verwenden stdoutoder stderrstattdessen stdin. stdinkönnte sehr gut eine Pfeife sein. Möglicherweise möchten Sie auch eine Zeile hinzufügen, z if not os.isatty(0): return float("inf").
mic_e

Das funktioniert irgendwie auf einem Chromebook-Terminal, das wie keine Funktionalität hat. +1
HyperNeutrino

39

Ich suchte herum und fand eine Lösung für Windows unter:

http://code.activestate.com/recipes/440694-determine-size-of-console-window-on-windows/

und eine Lösung für Linux hier.

Hier ist eine Version, die sowohl unter Linux, OS X als auch unter Windows / Cygwin funktioniert:

""" 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

Du hast mir die Zeit gespart, dies selbst zu tun. Funktioniert unter Linux. Sollte auch unter Windows funktionieren. Vielen Dank!
Steve V.

29

Es ist entweder:

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

Die shutilFunktion ist nur ein Wrapper um oseinen, der einige Fehler abfängt und einen Fallback einrichtet. Sie hat jedoch eine große Einschränkung: Sie bricht beim Piping! , das ist eine ziemlich große Sache.
Verwenden Sie os.get_terminal_size(0)stattdessen die Anschlussgröße für Rohrleitungen .

Das erste Argument 0ist ein Argument, das angibt, dass der Stdin-Dateideskriptor anstelle des Standard-Stdout verwendet werden sollte. Wir möchten stdin verwenden, da sich stdout beim Piping von selbst löst, was in diesem Fall einen Fehler auslöst.

Ich habe versucht herauszufinden, wann es sinnvoll wäre, stdout anstelle von stdin zu verwenden, und habe keine Ahnung, warum dies hier eine Standardeinstellung ist.


3
os.get_terminal_size()wurde in Python 3.3
villapx

Ich habe zuerst versucht, Shutil, und es hat beim ersten Versuch funktioniert. Ich benutze Python 3.6+.
Dan

Die Verwendung von os.get_terminal_size(0)wird abstürzen, wenn Sie zu stdin leiten. Versuchen Sie:echo x | python3 -c 'import os; print(os.get_terminal_size(0))'
Ehabkost


6

Es sieht so aus, als gäbe es einige Probleme mit diesem Code, Johannes:

  • getTerminalSize muss import os
  • was ist env? sieht aus wie os.environ.

Warum auch wechseln linesund colsvor der Rückkehr? Wenn TIOCGWINSZund sttybeide linesdann sagen cols, sage ich, lass es so. Dies verwirrte mich gut 10 Minuten lang, bevor ich die Inkonsistenz bemerkte.

Sridhar, ich habe diesen Fehler nicht erhalten, als ich die Ausgabe weitergeleitet habe. Ich bin mir ziemlich sicher, dass es beim Versuch richtig erwischt wird - außer.

Pascal, "HHHH"funktioniert nicht auf meiner Maschine, aber "hh"funktioniert. Ich hatte Probleme, Dokumentation für diese Funktion zu finden. Es sieht so aus, als wäre es plattformabhängig.

Chochem, eingearbeitet.

Hier ist meine Version:

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)

Ich bin auch über das gewandert env, und es ist in der Tat env = os.environvon der akzeptierten Antwort .
Sdaau

6

Viele der hier beschriebenen Python 2-Implementierungen schlagen fehl, wenn beim Aufrufen dieses Skripts kein steuerndes Terminal vorhanden ist. Sie können sys.stdout.isatty () überprüfen, um festzustellen, ob es sich tatsächlich um ein Terminal handelt. Dies schließt jedoch eine Reihe von Fällen aus. Daher glaube ich, dass die Verwendung des integrierten Curses-Pakets die pythonischste Methode zum Ermitteln der Terminalgröße ist.

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

2

Ich habe von hier aus die Lösung ausprobiert, die Folgendes fordert stty size:

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

Dies schlug jedoch für mich fehl, weil ich an einem Skript arbeitete, das umgeleitete Eingaben auf stdin erwartet, und mich sttybeschweren würde, dass "stdin in diesem Fall kein Terminal ist".

Ich konnte es so machen:

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

1

Die Antwort von @ reannual funktioniert gut, aber es gibt ein Problem: Sie os.popen ist jetzt veraltet . Das subprocessModul sollte stattdessen verwendet werden. Hier ist eine Version des Codes von @ reannual, die subprocessdie Frage verwendet und direkt beantwortet (indem die Spaltenbreite direkt angegeben wird als int:

import subprocess

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

Getestet unter OS X 10.9


1

Versuchen Sie "Segen"

Ich suchte genau das Gleiche. Es ist sehr einfach zu bedienen und bietet Werkzeuge zum Färben, Stylen und Positionieren im Terminal. Was Sie brauchen, ist so einfach wie:

from blessings import Terminal

t = Terminal()

w = t.width
h = t.height

Funktioniert wie ein Zauber unter Linux. (Ich bin mir bei MacOSX und Windows nicht sicher)

Download und Dokumentation hier

oder Sie können es mit pip installieren:

pip install blessings

1

Wenn Sie Python 3.3 oder höher verwenden, würde ich das get_terminal_size()bereits empfohlene integrierte Gerät empfehlen. Wenn Sie jedoch mit einer älteren Version nicht weiterkommen und eine einfache, plattformübergreifende Methode suchen , können Sie Asciimatics verwenden . Dieses Paket unterstützt Versionen von Python bis 2.7 und verwendet ähnliche Optionen wie die oben vorgeschlagenen, um die aktuelle Terminal- / Konsolengröße zu ermitteln.

Konstruieren Sie einfach Ihre ScreenKlasse und verwenden Sie die dimensionsEigenschaft, um die Höhe und Breite zu ermitteln. Dies funktioniert nachweislich unter Linux, OSX und Windows.

Oh - und vollständige Offenlegung hier: Ich bin der Autor. Sie können also gerne eine neue Ausgabe eröffnen, wenn Sie Probleme haben, diese zum Laufen zu bringen.


0

Hier ist eine Version, die Linux- und Solaris-kompatibel sein sollte. Basierend auf den Beiträgen und Kommentaren von madchine . Benötigt das Unterprozessmodul.

def termize ():
    import shlex, subprocess, re
    output = subprocess.check_output (shlex.split ('/ bin / stty -a'))
    m = re.search ('Zeilen \ D + (? P \ d +); Spalten \ D + (? P \ d +);', Ausgabe)
    wenn m:
        return m.group ('Zeilen'), m.group ('Spalten')
    OSError auslösen ('Schlechte Antwort:% s'% (Ausgabe))
>>> termize ()
('40', '100')
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.