Cómo obtener el ancho de la ventana de la consola de Linux en Python

¿Hay una manera en Python para determinar mediante progtwigción el ancho de la consola? Me refiero al número de caracteres que caben en una línea sin ajuste, no al ancho de píxel de la ventana.

Editar

Buscando una solución que funcione en Linux

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

usa el comando ‘stty size’ que, según un hilo en la lista de correo de python, es razonablemente universal en linux. Abre el comando ‘tamaño stty’ como un archivo, ‘lee’ desde él, y usa una simple división de cadenas para separar las coordenadas.

A diferencia del valor os.environ [“COLUMNAS”] (al que no puedo acceder a pesar de usar bash como mi shell estándar), los datos también estarán actualizados, mientras que creo que os.environ [“COLUMNAS”] el valor solo sería válido durante el momento del lanzamiento del intérprete de python (suponga que el usuario redimensionó la ventana desde entonces).

No estoy seguro de por qué está en el módulo shutil , pero aterrizó allí en Python 3.3, consultando el tamaño del terminal de salida :

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

Una implementación de bajo nivel está en el módulo os.

Un backport ahora está disponible para Python 3.2 y más abajo:

utilizar

 import console (width, height) = console.getTerminalSize() print "Your terminal's width is: %d" % width 

EDIT : oh, lo siento. Esa no es una versión estándar de python, aquí está la fuente de console.py (no sé de dónde viene).

El módulo parece funcionar así: comprueba si termcap está disponible, cuando sí. Utiliza eso; si no, comprueba si el terminal admite una ioctl especial y eso no funciona, también verifica las variables de entorno que algunos shells exportan. Esto probablemente funcionará solo en 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]) 

El código anterior no devolvió el resultado correcto en mi linux porque winsize-struct tiene 4 cortos sin firma, no 2 cortos firmados:

 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 y hp deben contener el ancho y la altura de los píxeles, pero no lo hacen.

Busqué alrededor y encontré una solución para Windows en:

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

y una solución para linux aquí.

Así que aquí hay una versión que funciona tanto en linux, os x como 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 

Comenzando en Python 3.3 es sencillo: 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 

Es bien:

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

La función shutil es solo una envoltura alrededor del shutil operativo que shutil algunos errores y establece una reserva, sin embargo, tiene una gran advertencia: ¡ se rompe cuando se canaliza! , que es una gran cosa
Para obtener el tamaño de la terminal cuando la tubería use en su lugar os.get_terminal_size(0) .

El primer argumento 0 es un argumento que indica que se debe usar el descriptor de archivo estándar en lugar de la salida estándar predeterminada. Queremos usar stdin porque stdout se separa cuando se está canalizando, lo que aumenta en este caso genera un error …
He intentado averiguar cuándo tendría sentido usar stdout en lugar de un argumento estándar y no tengo idea de por qué es un valor predeterminado aquí.

Parece que hay algunos problemas con ese código, Johannes:

  • getTerminalSize necesita import os
  • que es env se parece a os.environ .

Además, ¿por qué cambiar de lines y lines antes de volver? Si TIOCGWINSZ y stty dicen lines luego cols , digo que lo dejen así. Esto me confundió por unos buenos 10 minutos antes de que notara la inconsistencia.

Sridhar, no recibí ese error cuando canalicé la salida. Estoy bastante seguro de que está siendo atrapado adecuadamente en el bash, excepto.

pascal, "HHHH" no funciona en mi máquina, pero "hh" sí funciona. Tuve problemas para encontrar documentación para esa función. Parece que depende de la plataforma.

chochem, incorporado.

Aquí está mi versión:

 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) 

Muchas de las implementaciones de Python 2 aquí fallarán si no hay un terminal de control al llamar a este script. Puede verificar sys.stdout.isatty () para determinar si esto es en realidad un terminal, pero eso excluirá un montón de casos, por lo que creo que la forma más pirónica de averiguar el tamaño del terminal es utilizar el paquete de curses integrado.

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

Desde aquí estaba probando la solución que llama al stty size :

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

Sin embargo, esto me falló porque estaba trabajando en un script que espera una entrada redirigida en stdin, y stty se quejaría de que “stdin no es un terminal” en ese caso.

Pude hacerlo funcionar así:

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

La respuesta de @ reannual funciona bien, pero hay un problema con eso: os.popen ahora está en desuso . El módulo de subprocess debe usarse en su lugar, por lo que aquí hay una versión del código de @ reannual que usa el subprocess y responde directamente a la pregunta (dando el ancho de la columna directamente como un int :

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

Probado en OS X 10.9

Prueba “bendiciones”

Estaba buscando lo mismo. Es muy fácil de usar y ofrece herramientas para colorear, estilizar y posicionar en el terminal. Lo que necesitas es tan fácil como:

 from blessings import Terminal t = Terminal() w = t.width h = t.height 

Funciona como un encanto en Linux. (No estoy seguro de MacOSX y Windows)

Descarga y documentación aquí.

o puedes instalarlo con pip:

 pip install blessings 

Si está usando Python 3.3 o superior, recomendaría el get_terminal_size() como ya se recomendó. Sin embargo, si está atascado con una versión anterior y desea una forma simple y multiplataforma de hacerlo, podría usar asciimatics . Este paquete admite versiones de Python de regreso a 2.7 y usa opciones similares a las sugeridas anteriormente para obtener el tamaño actual de la terminal / consola.

Simplemente construya su clase de Screen y use la propiedad de las dimensions para obtener la altura y el ancho. Se ha comprobado que esto funciona en Linux, OSX y Windows.

Ah, y la revelación completa aquí: yo soy el autor, así que no dude en abrir un nuevo número si tiene algún problema para que esto funcione.

Aquí hay una versión que debería ser compatible con Linux y Solaris. Basado en los mensajes y comentarios de madchine . Requiere el módulo de subproceso.

 def términos de tamaño ():
     importar shlex, subprocess, re
     output = subprocess.check_output (shlex.split ('/ bin / stty -a'))
     m = re.search ('rows \ D + (? P \ d +); columnas \ D + (? P \ d +);', salida)
     si m:
         devolver m.group ('filas'), m.group ('columnas')
     raise OSError ('Mala respuesta:% s'% (salida))
 >>> Termsize ()
 ('40', '100')