web-dev-qa-db-ja.com

PythonでLinuxコンソールウィンドウの幅を取得する方法

pythonにコンソールの幅をプログラムで決定する方法はありますか?ウィンドウのピクセル幅ではなく、折り返さずに1行に収まる文字数を意味します。

編集

Linuxで動作するソリューションを探しています

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

pythonメーリングリストのスレッド によれば、Linuxではかなり汎用的な「stty size」コマンドを使用します。 「stty size」コマンドをファイルとして開き、「読み取り」、単純な文字列分割を使用して座標を分離します。

Os.environ ["COLUMNS"]値(標準のシェルとしてbashを使用しているにもかかわらずアクセスできない)とは異なり、os.environ ["COLUMNS"]を信じているのにデータも最新のものになります。値はpythonインタープリターの起動時のみ有効です(それ以降、ユーザーがウィンドウのサイズを変更したと仮定します)。

240
brokkr

なぜそれがモジュール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以下で利用可能になりました。

223
Gringo Suave

つかいます

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

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

EDIT:ああ、ごめんなさい。これはpython標準ライブラリの1つではなく、console.pyのソースです(どこから来たのかわかりません)。

モジュールはそのように動作するようです:はいの場合、termcapが利用可能かどうかをチェックします。それを使用します。 noの場合、端末が特別な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])
65
Johannes Weiss

上記のコードは、私のLinuxでは正しい結果を返しませんでした。なぜなら、winsize-structには、2つの署名されたショートではなく、4つの署名されていないショートがあるからです。

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にはピクセルの幅と高さを含める必要がありますが、含めないでください。

54
pascal

私は周りを検索して、ウィンドウの解決策を見つけました:

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 __== "__main__":
    sizex,sizey=getTerminalSize()
    print  'width =',sizex,'height =',sizey
38
Harco Kuppens

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
Bob Enohp

次のいずれかです。

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

shutil関数は、いくつかのエラーをキャッチしてフォールバックを設定するos関数の単なるラッパーですが、1つの大きな警告があります-パイピングすると壊れます! 、これは非常に大きな問題です。
パイピング時に端子サイズを取得するには、代わりにos.get_terminal_size(0)を使用します。

最初の引数0は、デフォルトのstdoutの代わりにstdinファイル記述子を使用する必要があることを示す引数です。 stdoutがパイプされているときにstdoutがそれ自体をデタッチするため、stdinを使用します。この場合、エラーが発生します。
stdin引数の代わりにstdoutを使用するのが理にかなっているのはいつかを考えてみましたが、なぜここでデフォルトになるのかわかりません。

14
Granitosaurus

そのコードにはいくつかの問題があるようです、ヨハネス:

  • getTerminalSizeimport osにする必要があります
  • envとは何ですか? os.environのように見えます。

また、戻る前にlinescolsを切り替えるのはなぜですか? TIOCGWINSZsttyの両方がlinesに続いてcolsと言う場合、そのままにしておきます。これにより、矛盾に気付く前に10分間混乱しました。

Sridhar、出力をパイプしたときにそのエラーは発生しませんでした。 try-exceptで適切にキャッチされていると確信しています。

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)
6
thejoshwolfe

このスクリプトを呼び出すときに制御端末がない場合、ここのPython 2実装の多くは失敗します。 sys.stdout.isatty()をチェックして、これが実際に端末であるかどうかを判断できますが、これは多くの場合を除外するため、端末サイズを判断する最もPython的な方法は組み込みのcursesパッケージを使用することです。

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

私はここから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
Marc Liyanage

Python 3.3以降を使用している場合、既に推奨されているように、組み込みのget_terminal_size()をお勧めします。ただし、古いバージョンにこだわっており、これを行うためのシンプルでクロスプラットフォームな方法が必要な場合は、 asciimatics を使用できます。このパッケージはPythonのバージョン2.7をサポートし、上記と同様のオプションを使用して現在のターミナル/コンソールサイズを取得します。

Screenクラスを構築し、dimensionsプロパティを使用して高さと幅を取得するだけです。これは、Linux、OSX、およびWindowsで動作することが実証されています。

ああ-ここに完全に開示されています:私は著者ですので、これを機能させるのに問題がある場合は、気軽に新しい問題を開いてください。

1
Peter Brittain

@reannualの答えはうまくいきますが、問題があります:os.popen現在廃止予定 。代わりにsubprocessモジュールを使用する必要があります。したがって、これはsubprocessを使用し、質問に直接回答する(列幅をintとして直接指定する)@reannualのコードのバージョンです。

import subprocess

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

OS X 10.9でテスト済み

1
rickcnagy

試してみてください "blessings"

私はまったく同じものを探していました。非常に使いやすく、ターミナルでの色付け、スタイリング、および配置のためのツールを提供します。必要なものは簡単です:

from blessings import Terminal

t = Terminal()

w = t.width
h = t.height

Linuxの魅力のように機能します。 (MacOSXとWindowsについてはわかりません)

ダウンロードとドキュメント こちら

または、pipでインストールできます。

pip install blessings
1
Iman Akbari

LinuxおよびSolarisと互換性のあるバージョンを以下に示します。 madchine からの投稿とコメントに基づいています。サブプロセスモジュールが必要です。

 def termsize():
 import shlex、サブプロセス、re 
 output = subprocess.check_output(shlex.split( '/ bin/stty -a'))
 m = re.search( 'rows\D +(?P\d +); columns\D +(?P\d +);'、出力)
 if m:
 return m.group( 'rows')、m.group( 'columns')
 raise OSError( 'Bad response:%s'%(output))
 >>> termsize()
( '40'、 '100')
0
Derrick Petzold