¿Cómo encuentro el ancho y alto de una ventana de terminal?

295

Como un ejemplo simple, quiero escribir un script CLI que pueda imprimir =en todo el ancho de la ventana del terminal.

#!/usr/bin/env php
<?php
echo str_repeat('=', ???);

o

#!/usr/bin/env python
print '=' * ???

o

#!/usr/bin/env bash
x=0
while [ $x -lt ??? ]; do echo -n '='; let x=$x+1 done; echo
demasiado php
fuente
Creé esta pequeña librería node.js para obtener el tamaño correcto de ventana npmjs.com/package/window-size
jonschlinkert

Respuestas:

562
  • tput cols te dice el número de columnas.
  • tput lines te dice el número de filas.
TonyUser
fuente
11
echo -e "lines\ncols"|tput -Spara obtener tanto las líneas como los cols, consulte: linux.about.com/library/cmd/blcmdl1_tput.htm
nickl-
44
tputes un gran comando con muchos comandos para leer el estado del terminal, controlar el cursor y las propiedades del texto, etc.
Drew Noakes
2
Práctico alias, por ejemplo:, alias dim="echo $(tput cols)x$(tput lines)"que podría resultar en 80x50.
obispo
2
Estas preguntas y respuestas probablemente pertenecen a los sitios SE de Unix o Superusuario.
mydoghasworms
2
@bishop el comando alias que proporcionó se evalúa cuando se obtiene el shell. Debe usar comillas simples para el comando alias. Me gusta así: alias dim='echo Terminal Dimensions: $(tput cols) columns x $(tput lines) rows'
brandonsimpkins
103

En bash, las variables ambientales $LINESy $COLUMNSdeberían poder hacer el truco. El se configurará automáticamente ante cualquier cambio en el tamaño del terminal. (es decir, la señal SIGWINCH )

David Dean
fuente
18
Sin embargo, estas variables de entorno solo están disponibles para bash, y no para ningún programa que se ejecute dentro de bash (como perl, python, ruby).
Br
99
Eso no funciona en nada más que en la sesión bash interactiva (si ejecuta el script, ya no es interactivo). El único lugar donde puede usarlo en un script es el prompt_command en bash.
Doncho Gunchev
1
En realidad, funciona en scripts no interactivos, si configura la checkwinsizeopción. Por ejemplo, este script no interactivo imprimirá las dimensiones del terminal en el que se ejecuta: shopt -s checkwinsize; (:); echo $LINES $COLUMNS(la checkwinsizeopción solo inicializa las variables después de esperar a que termine una subshell, por eso necesitamos la (:)declaración)
user3340662
$LINESy $COLUMNSse actualizan después de que SIGWINCHse envía, en realidad después de cualquier ejecución de comando interactivo. Si intenta actualizar PS1con trap SIGWINCHno puede usar $LINESy $COLUMNS, mantienen los valores antiguos ((
gavenkoa
LINESy COLUMNSsolo se establecen como variables de shell por bash. Bash no los establecerá como variables de entorno , a menos que exporte estas variables de shell.
Markus Kuhn
67

Y hay stty, de coreutils

$ stty size
60 120 # <= sample output

Imprimirá el número de filas y columnas, o alto y ancho, respectivamente.

Luego puede usar cualquiera cuto awkextraer la parte que desea.

Eso es stty size | cut -d" " -f1para la altura / líneas y stty size | cut -d" " -f2para el ancho / columnas

ryenus
fuente
Este estilo no puede funcionar con PIPE, sugiera usar el estilo tput.
liuyang1
66
El problema con tput es que no siempre está disponible, mientras que stty está disponible en cada tty. gracias por esa informacion!
iRaS
16
yes = | head -n$(($(tput lines) * $COLUMNS)) | tr -d '\n'
pixelbeat
fuente
3
No es una respuesta directa a la pregunta, sino un gran script de demostración.
Chris Page
¡Qué gran ejemplo!
Kurt Zhong
1
¿Cómo diablos me perdí el trcomando todos estos años? (facepalm)
Marco Massenzio
¿Qué lenguaje es este? Se parece vagamente a un script de shell con errores de sintaxis. (En el caparazón no puede haber espacios alrededor del signo igual de asignación, y la primera tubería parece fuera de lugar.)
triplicado
2
yes '='generará una cantidad infinita de líneas '=' y los siguientes comandos se organizarán lo suficiente como para llenar el terminal
pixelbeat
12

Para hacer esto en el entorno de la CLI de Windows, la mejor manera que puedo encontrar es usar el comando de modo y analizar la salida.

function getTerminalSizeOnWindows() {
  $output = array();
  $size = array('width'=>0,'height'=>0);
  exec('mode',$output);
  foreach($output as $line) {
    $matches = array();
    $w = preg_match('/^\s*columns\:?\s*(\d+)\s*$/i',$line,$matches);
    if($w) {
      $size['width'] = intval($matches[1]);
    } else {
      $h = preg_match('/^\s*lines\:?\s*(\d+)\s*$/i',$line,$matches);
      if($h) {
        $size['height'] = intval($matches[1]);
      }
    }
    if($size['width'] AND $size['height']) {
      break;
    }
  }
  return $size;
}

¡Espero que sea útil!

NOTA : La altura devuelta es el número de líneas en el búfer, no es el número de líneas visibles dentro de la ventana. ¿Hay mejores opciones por ahí?

Liceo
fuente
3
Tenga en cuenta un problema con esto: la salida de este comando es específica de la localidad. En otras palabras, esto no funcionará tal cual en otra configuración regional de Windows. Esto es lo que obtengo en Windows 7: i.imgur.com/Wrr7sWY.png
Camilo Martin
Se agregó una respuesta con una solución a eso. ¡+1 sin embargo!
Camilo Martin
10

En POSIX, en última instancia, desea invocar la llamada TIOCGWINSZ(Get WINdow SiZe) ioctl(). La mayoría de los idiomas deberían tener algún tipo de envoltorio para eso. Por ejemplo, en Perl puedes usar Término :: Tamaño :

use Term::Size qw( chars );

my ( $columns, $rows ) = chars \*STDOUT;
LeoNerd
fuente
1
Gracias por esto, me llevó en la dirección correcta. Elixir: :io.columnsErlang: io:columns(). erlang.org/doc/man/io.html#columns-0
Henrik N
2
No existe TIOCGWINSZen el estándar POSIX y ioctl()solo se define para la función STREAMS obsoleta.
osvein
4

Como mencioné en la respuesta de lyceus, su código fallará en Windows de configuración regional que no esté en inglés porque la salida de modepuede no contener las subcadenas "columnas" o "líneas":

                                         salida de comando de modo

Puede encontrar la subcadena correcta sin buscar texto:

 preg_match('/---+(\n[^|]+?){2}(?<cols>\d+)/', `mode`, $matches);
 $cols = $matches['cols'];

Tenga en cuenta que ni siquiera me estoy molestando con las líneas porque no es confiable (y en realidad no me importan).

Editar: Según los comentarios sobre Windows 8 (oh, tú ...), creo que esto puede ser más confiable:

 preg_match('/CON.*:(\n[^|]+?){3}(?<cols>\d+)/', `mode`, $matches);
 $cols = $matches['cols'];

Sin embargo, pruébelo porque no lo probé.

Camilo Martin
fuente
Tu método no funciona en Win8. Tengo más de una ---línea. i.imgur.com/4x02dqT.png
mpen
@ Mark Bueno, genial, eso es HERMOSO. Gracias Windows <3 (en una nota más relevante: veré cómo solucionar eso ... cuando salga Windows 9: P).
Camilo Martin
Esta es la manera de hacerlo: $mode = `mode`; list($rows, $cols) = array_slice(preg_split('/\n/', substr($mode, strpos($mode, 'CON:'))), 2, 2);. Y luego simplemente reemplazo todo menos los números.
Aleksandr Makov
@AleksandrMakov Me pregunto qué pasa si hay idiomas con orden como CON device status:. Quizás emparejar algo así CON.*:funcionaría mejor.
Camilo Martin
1
@ Mark En realidad me estaba preguntando exactamente eso. ¿Por qué diablos hice eso? En duda, simplemente asumí que había alguna razón y seguí con eso, jajaja.
Camilo Martin
1

Inspirado por la respuesta de @ pixelbeat, aquí hay una barra horizontal creada por un tputligero mal uso del printfrelleno / relleno ytr

printf "%0$(tput cols)d" 0|tr '0' '='
huoneusto
fuente
0

Hay algunos casos en los que sus filas / LÍNEAS y columnas no coinciden con el tamaño real del "terminal" que se está utilizando. Quizás no tenga un "tput" o "stty" disponible.

Aquí hay una función bash que puede usar para verificar visualmente el tamaño. Esto funcionará hasta 140 columnas x 80 filas. Puede ajustar los máximos según sea necesario.

function term_size
{
    local i=0 digits='' tens_fmt='' tens_args=()
    for i in {80..8}
    do
        echo $i $(( i - 2 ))
    done
    echo "If columns below wrap, LINES is first number in highest line above,"
    echo "If truncated, LINES is second number."
    for i in {1..14}
    do
        digits="${digits}1234567890"
        tens_fmt="${tens_fmt}%10d"
        tens_args=("${tens_args[@]}" $i)
    done
    printf "$tens_fmt\n" "${tens_args[@]}"
    echo "$digits"
}
pourhaus
fuente