Как мне найти ширину и высоту окна терминала?

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
слишком много PHP
источник
Я создал эту крошечную библиотеку node.js для постоянного получения правильного размера окна. Npmjs.com/package/window-size
jonschlinkert,

Ответы:

562
  • tput cols говорит вам количество столбцов.
  • tput lines говорит вам количество строк.
TonyUser
источник
11
echo -e "lines\ncols"|tput -Sчтобы узнать как строки, так и столбцы, смотрите: linux.about.com/library/cmd/blcmdl1_tput.htm
nickl
4
tputотличная команда с большим количеством команд для чтения состояния терминала, управления курсором и свойствами текста и так далее.
Дрю Ноакс
2
Удобный псевдоним, например:, alias dim="echo $(tput cols)x$(tput lines)"который может привести к 80x50.
епископ
2
Эти вопросы и ответы, вероятно, относятся к сайтам SE Unix или Superuser.
mydoghasworms
2
@bishop предоставленная вами команда псевдонима оценивается при получении оболочки. Вы должны использовать одинарные кавычки для команды псевдонима. Как и так: alias dim='echo Terminal Dimensions: $(tput cols) columns x $(tput lines) rows'
brandonsimpkins
103

В bash переменные $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опция инициализирует переменные только после ожидания завершения подоболочки, поэтому нам нужен (:)оператор)
user3340662
$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для ширины / столбцов

ryenus
источник
Этот стиль не может работать с PIPE, рекомендуется использовать стиль tput.
liuyang1
6
проблема с tput заключается в том, что он не всегда доступен, тогда как stty доступен в каждом tty. спасибо за эту информацию!
ИРАС
16
yes = | head -n$(($(tput lines) * $COLUMNS)) | tr -d '\n'
pixelbeat
источник
3
Не прямой ответ на вопрос, а отличный демонстрационный скрипт.
Крис Пейдж
Какой отличный пример!
Курт Жонг
1
Какого черта я пропустил trкоманду все эти годы? (
лицевая сторона
Какой это язык? Это выглядит смутно как сценарий оболочки с ошибками синтаксиса. (В оболочке не должно быть пробелов вокруг знака равенства, и первая труба кажется неуместной.)
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
Камило Мартин
Добавил ответ с решением для этого. +1 хотя!
Камило Мартин
10

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

use Term::Size qw( chars );

my ( $columns, $rows ) = chars \*STDOUT;
LeoNerd
источник
1
Спасибо за это - привел меня в правильном направлении. Эликсир: :io.columnsЭрланг: io:columns(). erlang.org/doc/man/io.html#columns-0
Хенрик N
2
В TIOCGWINSZстандарте POSIX его нет, и ioctl()он определен только для устаревшей функции STREAMS.
17
4

Как я уже упоминал в ответе лицея, его код не будет работать в неанглийской локали Windows, потому что тогда выходные данные modeмогут не содержать подстрок «столбцы» или «строки»:

                                         вывод команды 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
mpen
@ Марк Ну, отлично, это просто КРАСИВО. Спасибо винде. <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
@ Марк, я на самом деле расспрашивал себя именно об этом. Какого черта я это сделал? В сомнении, я просто предположил, что была какая-то причина, и пошел с этим, смеется.
Камило Мартин
1

Вдохновленный ответом @ pixelbeat, вот горизонтальная полоса, появившаяся благодаря tputнебольшому неправильному использованию printfотступов / заливки иtr

printf "%0$(tput cols)d" 0|tr '0' '='
huoneusto
источник
0

В некоторых случаях ваши строки / строки и столбцы не соответствуют фактическому размеру используемого «терминала». Возможно, у вас нет доступных «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"
}
pourhaus
источник