如何找到终端窗口的宽度和高度?

Posted

技术标签:

【中文标题】如何找到终端窗口的宽度和高度?【英文标题】:How do I find the width & height of a terminal window? 【发布时间】:2010-09-20 19:17:27 【问题描述】:

作为一个简单的例子,我想编写一个 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 本文涵盖以下 3 个主要答案,全部集中在一处:howto.lintel.in/find-width-height-terminal-linux。 【参考方案1】: tput cols 告诉你列数。 tput lines 告诉你行数。

【讨论】:

echo -e "lines\ncols"|tput -S 获取行和列参见:linux.about.com/library/cmd/blcmdl1_tput.htm tput 是一个很棒的命令,lots of commands 用于读取终端状态、控制光标和文本属性等等。 方便的别名,例如:alias dim="echo $(tput cols)x$(tput lines)",这可能会导致80x50 此问答可能属于 unix 或超级用户 SE 站点。 @bishop 您提供的别名命令会在 shell 获取源时进行评估。您需要为 alias 命令使用单引号。像这样:alias dim='echo Terminal Dimensions: $(tput cols) columns x $(tput lines) rows'【参考方案2】:
yes = | head -n$(($(tput lines) * $COLUMNS)) | tr -d '\n'

【讨论】:

不是直接回答这个问题,而是一个很棒的演示脚本。 多么好的例子! 这些年来我怎么会错过tr 命令? (捂脸) yes '=' 将输出无限数量的 '=' 行,并且以下命令组织起来足以填满终端 很好的例子;如果你想要一个更加神秘的 bashism:eval printf '=%.0s' 1..$[$COLUMNS*$LINES].【参考方案3】:

在 bash 中,$LINES$COLUMNS 环境变量应该可以解决问题。将在终端大小发生任何变化时自动设置。 (即SIGWINCH 信号)

【讨论】:

但是,这些环境变量仅对 bash 可用,对任何在 bash 中运行的程序(如 perl、python、ruby)均不可用。 这在交互式 bash 会话中不起作用(如果您运行脚本,它不再是交互式的)。您可以在脚本中使用它的唯一地方是 bash 中的 prompt_command。 实际上,如果您设置了checkwinsize 选项,它确实可以在非交互式脚本中工作。例如,这个非交互式脚本将打印运行它的终端的尺寸:shopt -s checkwinsize; (:); echo $LINES $COLUMNScheckwinsize 选项仅在等待子外壳完成后初始化变量,这就是我们需要@987654327 的原因@声明) $LINES$COLUMNS 在发送 SIGWINCH 之后更新,实际上是在任何交互式命令执行之后。如果您尝试使用trap SIGWINCH 更新PS1,则不能使用$LINES$COLUMNS,它们会保留旧值(( LINESCOLUMNS 仅由 bash 设置为 shell 变量。 Bash 不会将它们设置为环境变量,除非你导出这些shell 变量。【参考方案4】:

要在 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;

希望有用!

注意:返回的高度是缓冲区中的行数,而不是窗口中可见的行数。还有更好的选择吗?

【讨论】:

注意这个问题:这个命令的输出是特定于语言环境的。换句话说,这在另一个 Windows 语言环境中不会按原样工作。这就是我在 Windows 7 上得到的:i.imgur.com/Wrr7sWY.png 添加了解决方案的答案。虽然 +1!【参考方案5】:

在 POSIX 上,最终您希望调用 TIOCGWINSZ (Get WINdow SiZe) ioctl() 调用。大多数语言应该有某种包装器。例如在 Perl 中你可以使用Term::Size:

use Term::Size qw( chars );

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

【讨论】:

谢谢你——让我朝着正确的方向前进。 Elixir::io.columnsErlang:io:columns().erlang.org/doc/man/io.html#columns-0 POSIX 标准中没有 TIOCGWINSZioctl() 仅定义用于过时的 STREAMS 功能。【参考方案6】:

正如我在 lyceus 的回答中提到的,他的代码在非英语语言环境 Windows 上会失败,因为 mode 的输出可能不包含子字符串“columns”或“lines”:

                                       

不用找文字也能找到正确的子串:

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

请注意,我什至不关心线路,因为它不可靠(而且我实际上并不关心它们)。

编辑:根据 cmets 关于 Windows 8 的说法(哦,你……),我认为这可能更可靠:

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

尽管测试一下,因为我没有测试过。

【讨论】:

您的方法在 Win8 中不起作用。我得到了不止一个--- 行。 i.imgur.com/4x02dqT.png @Mark 好吧,太棒了,那真是太棒了。谢谢视窗。 我就是这样做的:$mode = `mode`; list($rows, $cols) = array_slice(preg_split('/\n/', substr($mode, strpos($mode, 'CON:'))), 2, 2);。然后我只替换除数字之外的所有内容。 @AleksandrMakov 我想知道如果有像CON device status: 这样的顺序语言会发生什么?也许匹配 CON.*: 这样的东西会更好。 @Mark 我实际上是在质疑自己这件事。我为什么要这么做?有疑问,我只是假设有某种原因并接受它,哈哈。【参考方案7】:

还有stty,见stty: Print or change terminal characteristics,更具体地说是Special settings

$ stty size
60 120 # <= sample output

它将分别打印行数和列数,或高度和宽度。

然后您可以使用cutawk 来提取您想要的部分。

stty size | cut -d" " -f1 表示高度/线条,stty size | cut -d" " -f2 表示宽度/列

【讨论】:

这种风格不能和PIPE一起使用,建议使用tput风格。 tput 的问题在于它并不总是可用,而 stty 在每个 tty 中都可用。谢谢你的信息! stty 不是来自 coreutils。 stty 是 POSIX 标准,因此几乎可以在任何地方使用,在绝对没有 coreutil 的 BSD 系统上也是如此,Coreutils 只是实现了大部分 POSIX 终端标准。【参考方案8】:

受@pixelbeat 回答的启发,这是tput 带来的水平条,对printf padding/filling 和tr 的轻微误用

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

【讨论】:

【参考方案9】:

在某些情况下,您的行/LINES 和列与正在使用的“终端”的实际大小不匹配。也许您可能没有可用的“tput”或“stty”。

这是一个 bash 函数,您可以使用它来直观地检查大小。这最多可以工作 140 列 x 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="$digits1234567890"
        tens_fmt="$tens_fmt%10d"
        tens_args=("$tens_args[@]" $i)
    done
    printf "$tens_fmt\n" "$tens_args[@]"
    echo "$digits"

【讨论】:

【参考方案10】:

获取窗口宽度

这个shell代码使一个全局变量$TERM_SIZE跟踪终端窗口的大小:

set_term_size() 
    TERM_SIZE="$(stty size 2>/dev/null)" && [ "$TERM_SIZE" ] ||
        TERM_SIZE='25 80'

trap set_term_size WINCH
set_term_size

它会尝试stty size,然后再假设终端高 25 行,宽 80 个字符。 POSIX 确实要求 size 操作数为 stty`,因此需要回退。

然后您可以使用 shell 的有限字符串替换功能来访问 columsn 参数:

echo "$TERM_SIZE% *" # Prints the terminal's height.
echo "$TERM_SIZE#* " # Prints the terminal's width.

当然,您使用的脚本语言可能会提供一个库来为您处理这些问题——您应该使用它。

打印一行

一旦你知道终端的宽度,打印一条水平线就很容易了,例如,通过滥用printf的字符串填充:

printf '%*s\n' "$TERM_SIZE#* " '' | 
tr ' ' -

第一行告诉printf 打印与管道一样多的空格(通过滥用字符串填充)。请注意,POSIX 确实没有提到* 语法,所以这可能不像上面的代码那样可移植。

第二行告诉tr 从该管道中读取数据并用连字符替换每个空格。

【讨论】:

以上是关于如何找到终端窗口的宽度和高度?的主要内容,如果未能解决你的问题,请参考以下文章

Jquery如何获取浏览器窗口宽度?

如何使用 ioctl 获取终端的宽度和高度?

如何在引导程序中获取窗口的高度和宽度

如何获取屏幕的宽度

将视图添加到窗口时如何获取视图宽度和高度?

如何使用 ExtJs 制作 100% 高度和宽度的窗口?