Comment trouver la largeur et la hauteur d'une fenêtre de terminal?


295

À titre d'exemple simple, je veux écrire un script CLI qui peut imprimer =sur toute la largeur de la fenêtre du terminal.

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

ou

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

ou

#!/usr/bin/env bash
x=0
while [ $x -lt ??? ]; do echo -n '='; let x=$x+1 done; echo

J'ai créé cette petite bibliothèque node.js pour obtenir systématiquement la taille de fenêtre correcte npmjs.com/package/window-size
jonschlinkert

Réponses:


562
  • tput cols vous indique le nombre de colonnes.
  • tput lines vous indique le nombre de lignes.

11
echo -e "lines\ncols"|tput -Spour obtenir à la fois les lignes et les colonnes,
nickl-

4
tputest une excellente commande avec de nombreuses commandes pour lire l'état du terminal, contrôler les propriétés du curseur et du texte, etc.
Drew Noakes du

2
Alias ​​pratique, par exemple alias dim="echo $(tput cols)x$(tput lines)":, ce qui pourrait entraîner 80x50.
évêque

2
Ce Q&A appartient probablement aux sites SE Unix ou superutilisateur.
mydoghasworms

2
@bishop la commande d'alias que vous avez fournie est évaluée lorsque le shell est obtenu. Vous devez utiliser des guillemets simples pour la commande d'alias. Comme ça: alias dim='echo Terminal Dimensions: $(tput cols) columns x $(tput lines) rows'
brandonsimpkins

103

En bash, les variables $LINESet l' $COLUMNSenvironnement devraient être capables de faire l'affaire. Le sera automatiquement réglé lors de tout changement de taille du terminal. (c'est-à-dire le signal SIGWINCH )


18
Cependant, ces variables d'environnement ne sont disponibles que pour bash, et pas pour les programmes qui s'exécutent dans bash (comme perl, python, ruby).
Br.Bill

9
Cela ne fonctionne que dans la session bash interactive (si vous exécutez le script, il n'est plus interactif). Le seul endroit où vous pouvez l'utiliser dans un script est la commande prompt_command dans bash.
Doncho Gunchev

1
En fait, cela fonctionne dans les scripts non interactifs, si vous définissez l' checkwinsizeoption. Par exemple, ce script non interactif affichera les dimensions du terminal sur lequel il est exécuté: shopt -s checkwinsize; (:); echo $LINES $COLUMNS(l' checkwinsizeoption initialise uniquement les variables après avoir attendu la fin d'un sous-shell, c'est pourquoi nous avons besoin de l' (:)instruction)
user3340662

$LINESet $COLUMNSsont mis à jour après l' SIGWINCHenvoi, en fait après toute exécution de commande interactive. Si vous essayez de mettre à jour PS1avec trap SIGWINCHvous ne pouvez pas utiliser $LINESet $COLUMNS, ils conservent les anciennes valeurs ((
gavenkoa

LINESet COLUMNSne sont définis comme variables shell que par bash. Bash ne les définira pas comme variables d'environnement , sauf si vous exportez ces variables shell.
Markus Kuhn

67

Et il y a stty, de coreutils

$ stty size
60 120 # <= sample output

Il imprimera le nombre de lignes et de colonnes, ou la hauteur et la largeur, respectivement.

Ensuite, vous pouvez utiliser soit cutou awkpour extraire la pièce que vous souhaitez.

C'est stty size | cut -d" " -f1pour la hauteur / lignes et stty size | cut -d" " -f2pour la largeur / colonnes


Ce style ne peut pas fonctionner avec PIPE, suggérez d'utiliser le style tput.
liuyang1

6
le problème avec tput est qu'il n'est pas toujours disponible alors que stty est disponible dans chaque tty. merci pour cette info!
iRaS

16
yes = | head -n$(($(tput lines) * $COLUMNS)) | tr -d '\n'

3
Pas une réponse directe à la question, mais un excellent script de démonstration.
Chris Page

Quel bel exemple!
Kurt Zhong

1
comment diable ai-je manqué la trcommande toutes ces années? (facepalm)
Marco Massenzio

Quelle langue est-ce? Il ressemble vaguement à un script shell avec des erreurs de syntaxe. (Dans le shell, vous ne pouvez pas avoir d'espaces autour du signe égal à l'affectation, et le premier tuyau semble hors de
propos

2
yes '='produira une quantité infinie de lignes '=' et les commandes suivantes organiseront suffisamment pour remplir le terminal
pixelbeat

12

Pour ce faire, dans l'environnement Windows CLI, la meilleure façon de trouver est d'utiliser la commande mode et d'analyser la sortie.

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;
}

J'espère que c'est utile!

Remarque : la hauteur renvoyée est le nombre de lignes dans le tampon, ce n'est pas le nombre de lignes qui sont visibles dans la fenêtre. De meilleures options là-bas?


3
Notez un problème avec ceci: la sortie de cette commande est spécifique aux paramètres régionaux. En d'autres termes, cela ne fonctionnera pas tel quel sur un autre environnement local Windows. Voici ce que j'obtiens sur Windows 7: i.imgur.com/Wrr7sWY.png
Camilo Martin

Ajout d'une réponse avec une solution à cela. +1 cependant!
Camilo Martin

10

Sur POSIX, vous voulez finalement invoquer l'appel TIOCGWINSZ(Get WINdow SiZe) ioctl(). La plupart des langues devraient avoir une sorte de wrapper pour cela. Par exemple, en Perl, vous pouvez utiliser Term :: Size :

use Term::Size qw( chars );

my ( $columns, $rows ) = chars \*STDOUT;

1
Merci pour cela - m'a conduit dans la bonne direction. Elixir: :io.columnsErlang: io:columns(). erlang.org/doc/man/io.html#columns-0
Henrik N

2
Il n'y TIOCGWINSZen a pas dans la norme POSIX et ioctl()n'est défini que pour la fonction STREAMS obsolète.
osvein

4

Comme je l'ai mentionné dans la réponse de lyceus, son code échouera sous Windows local non anglais car la sortie de modene peut pas contenir les sous-chaînes "colonnes" ou "lignes":

                                         sortie de commande de mode

Vous pouvez trouver la bonne sous-chaîne sans chercher de texte:

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

Notez que je ne me soucie même pas des lignes car elles ne sont pas fiables (et je m'en fous en fait).

Edit: Selon les commentaires sur Windows 8 (oh vous ...), je pense que cela peut être plus fiable:

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

Mais testez-le, car je ne l'ai pas testé.


Votre méthode ne fonctionne pas dans Win8. Je reçois plus d'une ---ligne. i.imgur.com/4x02dqT.png
mpen

@Mark Eh bien, génial, c'est juste BEAU. Merci Windows. <3 (sur une note plus pertinente: je vais voir comment résoudre ce problème ... lorsque Windows 9 sortira: P).
Camilo Martin

Ceci est la façon dont je le fais: $mode = `mode`; list($rows, $cols) = array_slice(preg_split('/\n/', substr($mode, strpos($mode, 'CON:'))), 2, 2);. Et puis je remplace tout sauf les chiffres.
Aleksandr Makov,

@AleksandrMakov Je me demande ce qui se passe s'il y a des langues avec ordre comme CON device status:? Peut-être que faire correspondre quelque chose comme ça CON.*:fonctionnerait mieux.
Camilo Martin

1
@Mark, je me posais en fait cette question exacte. Pourquoi diable ai-je fait ça? Dans le doute, j'ai juste supposé qu'il y avait une raison et je suis allé avec, lol.
Camilo Martin

1

Inspiré par la réponse de @ pixelbeat, voici une barre horizontale créée par une tputlégère mauvaise utilisation du printfrembourrage / remplissage ettr

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

0

Dans certains cas, vos lignes / lignes et colonnes ne correspondent pas à la taille réelle du "terminal" utilisé. Peut-être ne disposez-vous pas d'un "tput" ou d'un "stty".

Voici une fonction bash que vous pouvez utiliser pour vérifier visuellement la taille. Cela fonctionnera jusqu'à 140 colonnes x 80 lignes. Vous pouvez ajuster les maximums selon vos besoins.

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"
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.