Як знайти ширину та висоту вікна терміналу?


295

Як простий приклад, я хочу написати сценарій CLI, який може друкувати =по всій ширині вікна терміналу.

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

або

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

або

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

Я створив цю крихітну вкладку node.js для постійного отримання правильного розміру вікна npmjs.com/package/window-size
jonschlinkert

Відповіді:


562
  • tput cols повідомляє вам кількість стовпців.
  • tput lines повідомляє вам кількість рядків.

11
echo -e "lines\ncols"|tput -Sщоб отримати як рядки, так і значки,
nickl-

4
tputце чудова команда з великою кількістю команд для зчитування стану терміналу, керування властивостями курсору та тексту тощо.
Дрю Ноакс

2
Зручний псевдонім, наприклад:, alias dim="echo $(tput cols)x$(tput lines)"який може призвести до 80x50.
єпископ

2
Це питання та відповіді, ймовірно, належать або на сайтах unix, або на суперпользователях SE.
mydoghasworms

2
Команда @bishop alias, яку ви надали, оцінюється, коли оболонка отримує джерело. Для команди alias потрібно використовувати одинарні лапки. Ось так: alias dim='echo Terminal Dimensions: $(tput cols) columns x $(tput lines) rows'
brandonsimpkins

103

По суті, змінні $LINESта $COLUMNSсередовищі повинні бути в змозі зробити трюк. Значення буде встановлено автоматично при будь-якій зміні розміру терміналу. (тобто сигнал SIGWINCH )


18
Однак ці змінні середовища доступні лише для bash, а не для будь-яких програм, що працюють всередині bash (наприклад, perl, python, ruby).
Br.Bill

9
Це не працює ні в чому, окрім інтерактивного сеансу bash (якщо ви запускаєте сценарій, він більше не є інтерактивним). Єдине місце, де ви можете використовувати його у скрипті, - prompt_command в bash.
Дончо Гунчев

1
Насправді це працює в неінтерактивних сценаріях, якщо встановити checkwinsizeпараметр. Наприклад, цей неінтерактивний скрипт буде надрукувати розміри терміналу, на якому він запущений: shopt -s checkwinsize; (:); echo $LINES $COLUMNS( checkwinsizeопція ініціалізує змінні лише після того, як чекає завершення (:)
підпалубки,

$LINESі $COLUMNSоновлюються після SIGWINCHвідправки, фактично після будь-якого інтерактивного виконання команд. При спробі оновлення PS1з trap SIGWINCHвами не може використовувати $LINESі $COLUMNS, вони зберігають старі значення ((
gavenkoa

LINESі COLUMNSвстановлюються лише як змінні оболонки через bash. Bash не встановить їх як змінні середовища , якщо ви не експортуєте ці змінні оболонки.
Маркус Кун

67

А там stty, від coreutils

$ stty size
60 120 # <= sample output

Він надрукує кількість рядків і стовпців, або висоту та ширину відповідно.

Тоді ви можете використовувати cutабо awkвитягти потрібну частину.

Це stty size | cut -d" " -f1для висоти / ліній та stty size | cut -d" " -f2ширини / стовпців


Цей стиль не може працювати з PIPE, пропоную використовувати стиль tput.
liuyang1

6
Проблема з tput полягає в тому, що він не завжди доступний, тоді як stty доступний у кожному tty. дякую за цю інформацію!
iRaS

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

3
Не пряма відповідь на питання, а чудовий демо-сценарій.
Кріс Пейдж

Який чудовий приклад!
Курт Чжун

1
як чорт я пропустив trкоманду всі ці роки? (facepalm)
Марко Массенціо

Яка мова це? Це нечітко виглядає як сценарій оболонки із синтаксичними помилками. (У оболонці у вас не може бути пробілів навколо знака присвоєння рівня, і перша труба здається поза місцем.)
tripleee

2
yes '='виведе нескінченну кількість рядків '=' і наступні команди організуються достатньо, щоб заповнити термінал
pixelbeat

12

Для цього в середовищі Windows CLI найкращий спосіб, який я можу знайти, - це використовувати команду mode і проаналізувати вихід.

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

Я сподіваюся, що це корисно!

ПРИМІТКА : Висота, що повертається, - це кількість рядків у буфері, це не кількість рядків, які видно у вікні. Будь-які кращі варіанти там?


3
Зверніть увагу на проблему з цим: вихід цієї команди залежить від локального значення. Іншими словами, це не працюватиме так, як є в іншій локальній системі Windows. Це те, що я отримую в Windows 7: i.imgur.com/Wrr7sWY.png
Camilo Martin

Додав відповідь з рішенням на це. +1, хоча!
Каміло Мартін

10

На POSIX в кінцевому підсумку ви хочете викликати виклик TIOCGWINSZ(Get WINdow SiZe) ioctl(). Більшість мов повинні мати якусь обгортку для цього. Наприклад, в Perl ви можете використовувати термін :: розмір :

use Term::Size qw( chars );

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

1
Дякую за це - повели мене в правильному напрямку. Еліксир: Ерланг:io.columns : io:columns(). erlang.org/doc/man/io.html#column-0
Генрік N

2
У TIOCGWINSZстандарті POSIX ioctl()немає, він визначений лише для застарілої функції STREAMS.
osvein

4

Як я вже згадував у відповіді ліцея, його код не працюватиме в не англійській мові Windows, оскільки тоді вихід modeможе не містити підрядків "стовпці" або "рядки":

                                         режим командного виводу

Ви можете знайти правильну підрядку, не шукаючи тексту:

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

Зауважте, що я навіть не турбуюся про лінії, тому що це ненадійно (і я насправді не хвилююся ними).

Редагувати: Відповідно до коментарів щодо Windows 8 (ой, ви ...), я думаю, що це може бути більш надійним:

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

Проте протестуйте це, бо я його не перевіряв.


Ваш метод не працює в Win8. Я отримую більше одного ---рядка. i.imgur.com/4x02dqT.png
квітня

@Mark Ну, чудово, це просто КРАСИВО. Дякую Windows. <3 (про більш релевантну примітку: я дізнаюся, як це виправити ... коли вийде Windows 9: P).
Каміло Мартін

Це так , як я це зробити: $mode = `mode`; list($rows, $cols) = array_slice(preg_split('/\n/', substr($mode, strpos($mode, 'CON:'))), 2, 2);. І тоді я просто замінюю все, крім цифр.
Олександр Маков

@AleksandrMakov Цікаво, що станеться, якщо є мови з порядком на зразок CON device status:? Можливо, відповідність чомусь подібному CON.*:працювала б краще.
Каміло Мартін

1
@Mark Я насправді допитував себе саме таку річ. Чому чорт зробив це? В сумніві, я просто припустив, що є якась причина, і пішов з нею, lol.
Каміло Мартін

1

Натхненний відповіддю @ pixelbeat, ось горизонтальна смужка, що з'явилася на світ tput, незначне неправильне використання printfпрокладки / наповнення таtr

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

0

Є деякі випадки, коли ваші рядки / LINES та стовпці не відповідають фактичному розміру використовуваного "терміналу". Можливо, у вас може бути недоступний "tput" або "stty".

Ось функція bash, за допомогою якої можна візуально перевірити розмір. Це буде працювати до 140 стовпців х 80 рядків. Ви можете коригувати максимум за потребою.

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"
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.