【问题标题】: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

【问题讨论】:

标签: terminal


【解决方案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="${digits}1234567890"
            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 从该管道中读取数据并用连字符替换每个空格。

      【讨论】:

        猜你喜欢
        • 2012-01-21
        • 1970-01-01
        • 2011-10-20
        • 1970-01-01
        • 2014-05-20
        • 1970-01-01
        • 1970-01-01
        • 2011-02-10
        • 1970-01-01
        相关资源
        最近更新 更多