如何找到终端窗口的宽度和高度?
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 $COLUMNS
(checkwinsize
选项仅在等待子外壳完成后初始化变量,这就是我们需要@987654327 的原因@声明)
$LINES
和 $COLUMNS
在发送 SIGWINCH
之后更新,实际上是在任何交互式命令执行之后。如果您尝试使用trap SIGWINCH
更新PS1
,则不能使用$LINES
和$COLUMNS
,它们会保留旧值((
LINES
和 COLUMNS
仅由 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.columns
Erlang:io:columns().
erlang.org/doc/man/io.html#columns-0
POSIX 标准中没有 TIOCGWINSZ
,ioctl()
仅定义用于过时的 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
它将分别打印行数和列数,或高度和宽度。
然后您可以使用cut
或awk
来提取您想要的部分。
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
从该管道中读取数据并用连字符替换每个空格。
【讨论】:
以上是关于如何找到终端窗口的宽度和高度?的主要内容,如果未能解决你的问题,请参考以下文章