Python'da Linux konsol pencere genişliği nasıl alınır


264

Python'da konsolun genişliğini programlı olarak belirlemenin bir yolu var mı? Pencerenin piksel genişliğine değil, bir satıra sığmayan karakter sayısını kastediyorum.

Düzenle

Linux'ta çalışan bir çözüm mü arıyorsunuz


"Kolona bağımlı" bir baskı mekanizmasına sahip olmak için daha kapsamlı bir çözüm bulmak için bu cevaba bakınız. stackoverflow.com/questions/44129613/…
Riccardo Petraglia

Yanıtlar:


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

python posta listesindeki bir iş parçacığına göre linux üzerinde oldukça evrensel olan 'stty size' komutunu kullanır . 'Stty size' komutunu bir dosya olarak açar, 'okur' ve koordinatları ayırmak için basit bir dize kullanır.

Os.environ ["COLUMNS"] değerinin (bash'ı standart kabuğum olarak kullanmamıza rağmen erişemiyorum) aksine, veriler de güncel olurken os.environ ["COLUMNS"] değeri yalnızca python yorumlayıcısının başlatılma zamanı için geçerli olacaktır (kullanıcının pencereyi o zamandan beri yeniden boyutlandırdığını varsayalım).

(@GringoSuave tarafından python 3.3 ve sonrasında bunun nasıl yapılacağı ile ilgili cevaba bakınız)


1
Eğer "size" geçmek "-a" yerine Solaris üzerinde çalışmak için bu alabilirsiniz. Noktalı virgülle ayrılmış çıktıda "satır = Y; sütunlar = X" olacaktır.
Joseph Garvin

5
BUM'da COLUMNS varsayılan olarak dışa aktarılmaz, bu yüzden os.environ ["COLUMNS"] çalışmaz.
Kjell Andreassen

38
rows, columns = subprocess.check_output(['stty', 'size']).split()biraz daha kısa, artı alt süreç gelecek
cdosborn

7
tput stty'den daha iyidir, çünkü stty PIPE ile çalışamaz.
liuyang1

5
rows, columns = subprocess.check_output(['stty', 'size']).decode().split()Py2 / 3 uyumluluğu için unicode dizeleri istiyorsanız
Bryce Guinta

264

Modülde neden olduğundan emin değilim shutil, ancak çıkış terminalinin boyutunu sorgulayarak Python 3.3'e indi :

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

OS modülünde düşük düzeyli bir uygulama vardır. Windows'da da çalışır.

Python 3.2 ve daha önceki sürümler için bir backport mevcut:


25
Çünkü artık 2.7 kullanmamalısınız, 3.x'e atlayın.
anthonyryan1

5
@osirisgothra Birçok barındırma sağlayıcısı henüz python3'ü desteklememektedir, bu nedenle bazılarımız arka uç gelişimi için python2 kullanmak zorunda kalıyoruz. Rağmen terminal boyutu alma ile ilgisi olmamalı ...
beyazsakal

4
@osirisgothra Çünkü çok fazla Python 2 kodu var. Ayrıca, Pypy hala oldukça zayıf Python 3 desteğine sahiptir.
Antimon

2
@whitebeard Abonenin Python 3'ü bir VPS'ye yükleyememesinin bir nedeni var mı? Veya 2016'da, hala yöneticisi Python 3'ü yüklemek istemeyen paylaşılan barındırma kullanıyor mu? Örneğin, WebFaction paylaşılan barındırma python3.5yüklendi.
Damian Yerrick

2
Bir dosyadan standart girdi yeniden yönlendirildiğinde de çalışan bir çözüm için +1! Diğer çözümlerle ya Inappropriate ioctl for devicehata / uyarı alıyordum ya da 80 tanımlı geri dönüş değerini
alıyordum

65

kullanım

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

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

EDIT : oh, özür dilerim. Bu bir python standart lib olanı değil, işte console.py kaynağı (nereden geldiğini bilmiyorum).

Modül şöyle çalışıyor: Varsa termcap, mevcut olup olmadığını kontrol eder . Bunu kullanır; hayır ise, terminalin özel bir ioctlçağrıyı destekleyip desteklemediğini kontrol eder ve bu da çalışmaz, bazı kabukların dışa aktardığı ortam değişkenlerini kontrol eder. Bu muhtemelen yalnızca UNIX üzerinde çalışacaktır.

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
Hızlı yanıt için teşekkürler, ancak burada ( effbot.org/zone/console-handbook.htm ) "Konsol modülü şu anda yalnızca Windows 95, 98, NT ve 2000 için kullanılabilir" diyor. Linux üzerinde çalışan bir çözüm arıyorum. Muhtemelen etiketten net değildi, soruyu buna göre düzenleyeceğim.
Sergey Golovchenko

2
Kullandığınız bu "konsol" modülü standart python kütüphanesinde olmadığından, kaynak kodunu veya en azından bir bağlantısını sağlamalısınız.
nosklo

Bunun hakkında çok üzgünüm. Aslında o modülü bilmiyordum. İthalat konsolunu denedim ve çalıştı, konsol kullandım. <tab> <tab> ve getTerminalSize () ortaya çıktı. Bunun yerine nerede ben zaten bir cevap yayınlanmıştır I basitlik ve çok şanslıydım çünkü bakmanın g
Johannes Weiss

Farklı bir "konsol" modülüne bakıyorum, lütfen sahip olduğunuz bağlantı için bir link verebilir misiniz?
Sergey Golovchenko

4
Oh, ve kod üzerinde kazık değil, ama "cr" kafa karıştırıcı (sütunlar, satırlar) ima çünkü kafa karıştırıcı bir isim. Gerçekte, tam tersi.
Paul Du Bois

57

Yukarıdaki kod linux'umda doğru sonucu döndürmedi çünkü winsize-struct'un 2 imzasız şortu var, 2 imzalı şortu yok:

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 ve hp piksel genişliği ve yüksekliği içermelidir, ancak içermemelidir.


4
Bu böyle yapılmalı; terminale yazdırmak istiyorsanız, dosya tanımlayıcısı (ioctl'in ilk argümanı) olarak '1' kullanmalısınız, çünkü stdin bir pipo ya da farklı bir tty olabilir.
mic_e

1
Belki de 0 ile değiştirilmelidirfcntl.ioctl(sys.stdin.fileno(), ...
raylu

4
bu en iyi cevaptır - kullanıcılarınız sadece terim genişliğini elde etmek için sürpriz bir alt işlem olmadığından mutlu olacaklar
zzzeek

4
bu gerçekten de en temiz cevap. Yine de kullanmanız gerektiğini stdoutya da stderryerine kullanmanız gerektiğini düşünüyorum stdin. stdinçok iyi bir boru olabilir. Gibi bir satır eklemek de isteyebilirsiniz if not os.isatty(0): return float("inf").
mic_e

bu bir şekilde işlevselliği olmayan bir chromebook terminalinde çalışır. +1
HyperNeutrino

39

Etrafı araştırdım ve Windows için bir çözüm buldum:

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

ve burada linux için bir çözüm.

İşte hem linux, os x hem de windows / cygwin üzerinde çalışan bir sürüm:

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

Bunu kendim yapmam için beni kurtardın. Linux üzerinde çalışır. Windows üzerinde de çalışmalıdır. Teşekkürler!
Steve V.

30

O da:

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

shutilFonksiyon etrafında sarıcı olduğunu osancak bir büyük ihtar vardır, bazı hatalar yakalar ve bir yedek özelliğini kurmak one - o zaman boru tesisatı kırar! Bu oldukça büyük bir anlaşma.
Borulama sırasında terminal boyutunu almak için os.get_terminal_size(0)kullanın.

İlk argüman 0, varsayılan stdout yerine stdin dosya tanımlayıcısının kullanılması gerektiğini belirten bir argüman. Stdin'i kullanmak istiyoruz çünkü stdout, boru döşenirken kendisini ayırır, bu durumda bir hata oluşturur.

Stdin argümanı yerine stdout kullanmanın ne zaman mantıklı olduğunu anlamaya çalıştım ve burada neden varsayılan olduğunu bilmiyorum.


3
os.get_terminal_size()Python 3.3'te tanıtıldı
villapx

İlk başta kapatmayı denedim ve ilk denemede çalıştı. Python 3.6+ kullanıyorum.
Dan

Kullanılması os.get_terminal_size(0)Eğer boru stdin'i eğer kilitlenmesine. Deneyin:echo x | python3 -c 'import os; print(os.get_terminal_size(0))'
ehabkost


6

Görünüşe göre bu kodla ilgili bazı sorunlar var Johannes:

  • getTerminalSize ihtiyacı olmak import os
  • nedir env? gibi görünüyor os.environ.

Ayrıca, neden geçmeli linesve colsgeri dönmeden önce? Eğer TIOCGWINSZve sttyikisi de demek lineso zaman cols, ben bu şekilde bırakmak demek. Bu tutarsızlığı fark etmeden önce 10 dakika boyunca beni şaşırttı.

Sridhar, çıktıyı borsalarken bu hatayı almadım. Eminim denemede düzgün bir şekilde yakalandığından eminim.

pascal, "HHHH"makinemde "hh"çalışmıyor , ama çalışıyor . Bu işlevin belgelerini bulmakta sorun yaşadım. Görünüşe göre platforma bağımlı.

chochem, dahil.

İşte benim versiyonum:

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)

Ben de dolaşıyordum envve gerçekten env = os.environde kabul edilen cevaptan .
sdaau

6

Bu komut dosyasını çağırdığınızda kontrol terminali yoksa Python 2 uygulamalarının çoğu başarısız olur. Bu aslında bir terminal olup olmadığını belirlemek için sys.stdout.isatty () kontrol edebilirsiniz, ancak bu bir grup durumda dışlayacak, bu yüzden terminal boyutunu anlamanın en pitonik yolunun yerleşik curses paketini kullanmak olduğuna inanıyorum.

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

2

Buradan çağıran çözümü deniyordum stty size:

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

Ancak bu benim için başarısız oldu çünkü stdin üzerine yeniden yönlendirilmiş girdi bekleyen bir komut dosyası üzerinde çalışıyordu ve sttybu durumda "stdin bir terminal değil" şikayet ediyorum.

Bu şekilde çalışmasını sağlayabildim:

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

2

"Bereket" i deneyin

Ben de aynı şeyi arıyordum. Kullanımı çok kolaydır ve terminalde renklendirme, şekillendirme ve konumlandırma için araçlar sunar. İhtiyacınız olan şey şu kadar kolaydır:

from blessings import Terminal

t = Terminal()

w = t.width
h = t.height

Linux'ta bir cazibe gibi çalışır. (MacOSX ve Windows hakkında emin değilim)

İndirme ve dokümantasyon burada

veya pip ile kurabilirsiniz:

pip install blessings

Günümüzde, "kutsamaların" şu anda sürdürülen bir çatalı (ve geliştirmesi) olan "kutsanmış" ı denemeliyim.
zezollo

1

@ reannual'ın cevabı iyi çalışıyor, ancak bununla ilgili bir sorun var: os.popen şimdi kullanımdan kaldırıldı . Bunun subprocessyerine modül kullanılmalıdır, bu yüzden subprocesssoruyu kullanan ve doğrudan cevaplayan @ reannual kodunun bir sürümü ( sütun genişliğini doğrudan şöyle vererek int:

import subprocess

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

OS X 10.9'da test edilmiştir


1

Python 3.3 veya üstünü kullanıyorsanız, dahili get_terminal_size()olarak tavsiye edilen şekilde öneriyorum. Bununla birlikte, daha eski bir sürümle sıkışıp kalırsanız ve bunu yapmanın basit, platformlar arası bir yolunu istiyorsanız, siyatik kullanabilirsiniz . Bu paket Python'un 2.7'ye kadar olan sürümlerini destekler ve geçerli terminal / konsol boyutunu elde etmek için yukarıda önerilenlere benzer seçenekler kullanır.

Sadece Screensınıfınızı oluşturun ve dimensionsyüksekliği ve genişliği elde etmek için özelliği kullanın. Bunun Linux, OSX ve Windows üzerinde çalıştığı kanıtlanmıştır.

Oh - ve tam açıklama burada: Ben yazarım, bu yüzden bu işe yarayan herhangi bir sorun varsa yeni bir sorun açmak için çekinmeyin.


0

Linux ve Solaris uyumlu olması gereken bir sürüm. Madchine yazı ve taahhütlerine dayanarak . Alt işlem modülünü gerektirir.

def termsize ():
    shlex, alt süreç, yeniden içe aktarma
    output = subprocess.check_output (shlex.split ('/ bin / stty -a'))
    m = yeniden arama ('satırlar \ D + (? P \ d +); sütunlar \ D + (? P \ d +);', çıktı)
    eğer m:
        m.group ('satırlar'), m.group ('sütunlar') döndür
    OSError değerini yükselt ('Kötü yanıt:% s'% (çıktı))
>>> termsiz ()
('40', '100')
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.