Come trovo la larghezza e l'altezza di una finestra terminale?


295

Come semplice esempio, voglio scrivere uno script CLI in grado di stampare =su tutta la larghezza della finestra del terminale.

#!/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

Ho creato questa piccola libreria node.js per ottenere costantemente la dimensione corretta della finestra npmjs.com/package/window-size
jonschlinkert

Risposte:


562
  • tput cols ti dice il numero di colonne.
  • tput lines ti dice il numero di righe.

11
echo -e "lines\ncols"|tput -Sper ottenere sia le righe che i caratteri, consultare: linux.about.com/library/cmd/blcmdl1_tput.htm
nickl-

4
tputè un ottimo comando con molti comandi per leggere lo stato del terminale, controllare le proprietà del cursore e del testo e così via.
Drew Noakes,

2
Alias ​​pratico, ad esempio alias dim="echo $(tput cols)x$(tput lines)":, che potrebbe risultare in 80x50.
vescovo,

2
Queste domande e risposte probabilmente appartengono ai siti SE unix o superuser.
mydoghasworms,

2
@bishop il comando alias che hai fornito viene valutato quando la shell viene approvata. È necessario utilizzare virgolette singole per il comando alias. In questo modo: alias dim='echo Terminal Dimensions: $(tput cols) columns x $(tput lines) rows'
brandonsimpkins,

103

In bash, le variabili ambientali $LINESe $COLUMNSdovrebbero essere in grado di fare il trucco. Verrà impostato automaticamente in caso di modifica delle dimensioni del terminale. (ovvero il segnale SIGWINCH )


18
Tuttavia, queste variabili di ambiente sono disponibili solo per bash e non per tutti i programmi eseguiti all'interno di bash (come perl, python, ruby).
Br.,

9
Ciò non funziona se non nella sessione bash interattiva (se si esegue lo script non è più interattivo). L'unico posto in cui puoi usarlo in uno script è il prompt_command in bash.
Doncho Gunchev,

1
In realtà, funziona con script non interattivi, se si imposta l' checkwinsizeopzione. Ad esempio, questo script non interattivo stamperà le dimensioni del terminale su cui viene eseguito: shopt -s checkwinsize; (:); echo $LINES $COLUMNS(l' checkwinsizeopzione inizializza le variabili solo dopo aver atteso il completamento di una subshell, motivo per cui abbiamo bisogno (:)
dell'istruzione

$LINESe $COLUMNSvengono aggiornati dopo l' SIGWINCHinvio, in realtà dopo l'esecuzione di qualsiasi comando interattivo. Se si tenta di aggiornare PS1con trap SIGWINCHnon è possibile utilizzare $LINESe $COLUMNS, mantengono vecchi valori ((
gavenkoa

LINESe COLUMNSsono impostati come variabili shell solo da bash. Bash non li imposterà come variabili di ambiente , a meno che non si esportino queste variabili di shell.
Markus Kuhn,

67

E c'è stty, dai coreutils

$ stty size
60 120 # <= sample output

Stampa il numero di righe e colonne o l'altezza e la larghezza, rispettivamente.

Quindi è possibile utilizzare cuto awkper estrarre la parte desiderata.

Questo è stty size | cut -d" " -f1per l'altezza / linee e stty size | cut -d" " -f2per la larghezza / colonne


Questo stile non può funzionare con PIPE, suggerisci di usare lo stile tput.
liuyang1,

6
il problema con tput è che non è sempre disponibile mentre stty è disponibile in ogni tty. grazie per quelle informazioni!
iRaS,

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

3
Non una risposta diretta alla domanda, ma un ottimo script demo.
Chris Page

Che grande esempio!
Kurt Zhong

1
come diamine ho perso il trcomando in tutti questi anni? (facepalm)
Marco Massenzio,

Che lingua è questa? Sembra vagamente uno script di shell con errori di sintassi. (Nella shell non è possibile avere spazi attorno al segno di assegnazione uguale e la prima pipe sembra fuori posto.)
Tripleee

2
yes '='produrrà un numero infinito di righe '=' e i seguenti comandi saranno abbastanza organizzati da riempire il terminale
pixelbeat

12

Per fare ciò in ambiente CLI di Windows, il modo migliore che posso trovare è utilizzare il comando mode e analizzare l'output.

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

Spero sia utile!

NOTA : l'altezza restituita è il numero di righe nel buffer, non è il numero di righe visibili all'interno della finestra. Qualche opzione migliore là fuori?


3
Nota un problema con questo: l'output di questo comando è specifico della locale. In altre parole, questo non funzionerà così com'è su un'altra locale di Windows. Questo è quello che ottengo su Windows 7: i.imgur.com/Wrr7sWY.png
Camilo Martin

Aggiunta una risposta con una soluzione. +1 però!
Camilo Martin,

10

Su POSIX, alla fine si desidera richiamare la chiamata TIOCGWINSZ(Get WINdow SiZe) ioctl(). La maggior parte delle lingue dovrebbe avere una sorta di wrapper per questo. Ad esempio in Perl puoi usare Term :: Size :

use Term::Size qw( chars );

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

1
Grazie per questo - mi ha portato nella giusta direzione. Elisir: :io.columnsErlang: io:columns(). erlang.org/doc/man/io.html#columns-0
Henrik N

2
Non esiste TIOCGWINSZuno standard POSIX ed ioctl()è definito solo per la funzione STREAMS obsoleta.
Osvein,

4

Come ho detto nella risposta di Lyceus, il suo codice non funzionerà su Windows con impostazioni internazionali non inglesi perché l'output di modepotrebbe non contenere le sottostringhe "colonne" o "linee":

                                         uscita comando modalità

Puoi trovare la sottostringa corretta senza cercare testo:

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

Nota che non mi preoccupo nemmeno delle linee perché è inaffidabile (e in realtà non mi interessa).

Modifica: secondo i commenti su Windows 8 (oh tu ...), penso che questo potrebbe essere più affidabile:

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

Prova comunque, perché non l'ho provato.


Il tuo metodo non funziona in Win8. Ottengo più di una ---riga. i.imgur.com/4x02dqT.png
mpen

@Mark Bene, fantastico, è semplicemente BELLISSIMO. Grazie a Windows. <3 (su una nota più pertinente: vedrò come risolverlo ... quando esce Windows 9: P).
Camilo Martin,

Questo è il modo in cui lo faccio: $mode = `mode`; list($rows, $cols) = array_slice(preg_split('/\n/', substr($mode, strpos($mode, 'CON:'))), 2, 2);. E poi sostituisco solo tutto tranne i numeri.
Aleksandr Makov,

@AleksandrMakov Mi chiedo cosa succede se ci sono lingue con un ordine simile CON device status:? Forse abbinare qualcosa del genere CON.*:avrebbe funzionato meglio.
Camilo Martin,

1
@Mark In realtà mi stavo interrogando su quella cosa esatta. Perché diamine l'ho fatto? Nel dubbio, ho solo pensato che ci fosse qualche motivo e ci sono andato, lol.
Camilo Martin,

1

Ispirato dalla risposta di @ pixelbeat, ecco una barra orizzontale creata da un tputleggero uso improprio di printfimbottitura / riempimento etr

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

0

Ci sono alcuni casi in cui le tue righe / LINEE e colonne non corrispondono alla dimensione effettiva del "terminale" in uso. Forse potresti non avere un "tput" o "stty" disponibile.

Ecco una funzione bash che puoi usare per controllare visivamente le dimensioni. Questo funzionerà fino a 140 colonne x 80 righe. È possibile regolare i massimi secondo necessità.

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"
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.